Repository: matrixorigin/matrixone
Pull request #4449 (2022-08-10 15:11:48)

logservice: use reverse proxy for service discovery
## 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 #

## What this PR does / why we need it:

Log Service's ClientConfig and HAKeeper's HAKeeperClientConfig both have an optional DiscoveryAddress field which is the service address of a reverse proxy provided by k8s. The reverse proxy redirects requests to a random Log Service node and we use this mechanism to query Log Shard info from such random node. As such shard info is broadcasted to all Log Service nodes via gossip over time, this allows eventual discovery of the details of the interested Log Service shard.  

This feature is required as replicas of a Log Shard can migrate to a completely different set of nodes over time, a pre determined & fixed address list alone won't be enough to allow clients to connect to the interested Log Shard. 

lni: This PR touched auto-generated pb.go files from multiple owners when none of their .proto files were changed. 

Probably because we are all using different version of pb on our dev machines? 
lni: OK, thanks to the help from @zhangxu19830126 and @nnsgmsone, it was my installation of gogoprotobuf causing the problem. upgraded to v1.3.2 and it is much better now. 
fengttt: > I am OK with the change, but can you explain why we need this in commit message? I think I need a better overview and motivation of this change.

Never mind.   Now we don't use commit message, but use PR message.
lni: > > I am OK with the change, but can you explain why we need this in commit message? I think I need a better overview and motivation of this change.
> 
> Never mind. Now we don't use commit message, but use PR message.

Yes, the "What this PR does / why we need it" section of the PR message will become the body of the commit message once merged. 
guguducken: @mergifyio refresh
lni: @mergifyio refresh
mergify[bot]: @guguducken is not allowed to run commands
mergify[bot]: > refresh

#### ✅ Pull request refreshed



<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "refresh", "conclusion": "success"}
-*- Mergify Payload End -*-
-->

Patch:
diff --git a/pkg/common/moerr/error.go b/pkg/common/moerr/error.go
index 47aed5f31..f522df6c5 100644
--- a/pkg/common/moerr/error.go
+++ b/pkg/common/moerr/error.go
@@ -42,7 +42,8 @@ const (
 	INVALID_INPUT     = 3001
 
 	// Group 4: unexpected state
-	INVALID_STATE = 4000
+	INVALID_STATE         = 4000
+	LOG_SERVICE_NOT_READY = 4001
 
 	// group 5: rpc timeout
 	// ErrRPCTimeout rpc timeout
diff --git a/pkg/logservice/client.go b/pkg/logservice/client.go
index 9b723b23c..4d1e7a23c 100644
--- a/pkg/logservice/client.go
+++ b/pkg/logservice/client.go
@@ -29,6 +29,7 @@ import (
 )
 
 var (
+	ErrLogServiceNotReady = moerr.NewError(moerr.LOG_SERVICE_NOT_READY, "log service not ready")
 	// ErrDeadlineNotSet is returned when deadline is not set in the context.
 	ErrDeadlineNotSet = moerr.NewError(moerr.INVALID_INPUT, "deadline not set")
 	// ErrInvalidDeadline is returned when the specified deadline is invalid, e.g.
@@ -247,8 +248,48 @@ type client struct {
 	respPool *sync.Pool
 }
 
-func newClient(ctx context.Context,
-	cfg ClientConfig) (*client, error) {
+func newClient(ctx context.Context, cfg ClientConfig) (*client, error) {
+	client, err := connectToLogService(ctx, cfg.ServiceAddresses, cfg)
+	if client != nil && err == nil {
+		return client, nil
+	}
+	if len(cfg.DiscoveryAddress) > 0 {
+		return connectToLogServiceByReverseProxy(ctx, cfg.DiscoveryAddress, cfg)
+	}
+	if err != nil {
+		return nil, err
+	}
+	return nil, ErrLogServiceNotReady
+}
+
+func connectToLogServiceByReverseProxy(ctx context.Context,
+	discoveryAddress string, cfg ClientConfig) (*client, error) {
+	si, ok, err := GetShardInfo(discoveryAddress, cfg.LogShardID)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, ErrLogServiceNotReady
+	}
+	addresses := make([]string, 0)
+	leaderAddress, ok := si.Replicas[si.ReplicaID]
+	if ok {
+		addresses = append(addresses, leaderAddress)
+	}
+	for replicaID, address := range si.Replicas {
+		if replicaID != si.ReplicaID {
+			addresses = append(addresses, address)
+		}
+	}
+	return connectToLogService(ctx, addresses, cfg)
+}
+
+func connectToLogService(ctx context.Context,
+	targets []string, cfg ClientConfig) (*client, error) {
+	if len(targets) == 0 {
+		return nil, nil
+	}
+
 	pool := &sync.Pool{}
 	pool.New = func() interface{} {
 		return &RPCRequest{pool: pool}
@@ -263,7 +304,7 @@ func newClient(ctx context.Context,
 		respPool: respPool,
 	}
 	var e error
-	addresses := append([]string{}, cfg.ServiceAddresses...)
+	addresses := append([]string{}, targets...)
 	rand.Shuffle(len(cfg.ServiceAddresses), func(i, j int) {
 		addresses[i], addresses[j] = addresses[j], addresses[i]
 	})
@@ -470,10 +511,9 @@ func getRPCClient(ctx context.Context, target string, pool *sync.Pool) (morpc.RP
 	mf := func() morpc.Message {
 		return pool.Get().(*RPCResponse)
 	}
-	/*timeout, err := getTimeoutFromContext(ctx)
-	if err != nil {
-		return nil, err
-	}*/
+	// we set connection timeout to a constant value so if ctx's deadline is much
+	// larger, then we can ensure that all specified potential nodes have a chance
+	// to be attempted
 	codec := morpc.NewMessageCodec(mf, defaultWriteSocketSize)
 	bf := morpc.NewGoettyBasedBackendFactory(codec,
 		morpc.WithBackendConnectWhenCreate(),
diff --git a/pkg/logservice/hakeeper_client.go b/pkg/logservice/hakeeper_client.go
index 95cb9a9d9..3438a362c 100644
--- a/pkg/logservice/hakeeper_client.go
+++ b/pkg/logservice/hakeeper_client.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/matrixorigin/matrixone/pkg/common/moerr"
 	"github.com/matrixorigin/matrixone/pkg/common/morpc"
+	"github.com/matrixorigin/matrixone/pkg/hakeeper"
 	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
 )
 
@@ -220,6 +221,47 @@ type hakeeperClient struct {
 
 func newHAKeeperClient(ctx context.Context,
 	cfg HAKeeperClientConfig) (*hakeeperClient, error) {
+	client, err := connectToHAKeeper(ctx, cfg.ServiceAddresses, cfg)
+	if client != nil && err == nil {
+		return client, nil
+	}
+	if len(cfg.DiscoveryAddress) > 0 {
+		return connectByReverseProxy(ctx, cfg.DiscoveryAddress, cfg)
+	}
+	if err != nil {
+		return nil, err
+	}
+	return nil, ErrNotHAKeeper
+}
+
+func connectByReverseProxy(ctx context.Context,
+	discoveryAddress string, cfg HAKeeperClientConfig) (*hakeeperClient, error) {
+	si, ok, err := GetShardInfo(discoveryAddress, hakeeper.DefaultHAKeeperShardID)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, nil
+	}
+	addresses := make([]string, 0)
+	leaderAddress, ok := si.Replicas[si.ReplicaID]
+	if ok {
+		addresses = append(addresses, leaderAddress)
+	}
+	for replicaID, address := range si.Replicas {
+		if replicaID != si.ReplicaID {
+			addresses = append(addresses, address)
+		}
+	}
+	return connectToHAKeeper(ctx, addresses, cfg)
+}
+
+func connectToHAKeeper(ctx context.Context,
+	targets []string, cfg HAKeeperClientConfig) (*hakeeperClient, error) {
+	if len(targets) == 0 {
+		return nil, nil
+	}
+
 	pool := &sync.Pool{}
 	pool.New = func() interface{} {
 		return &RPCRequest{pool: pool}
@@ -234,8 +276,8 @@ func newHAKeeperClient(ctx context.Context,
 		respPool: respPool,
 	}
 	var e error
-	addresses := append([]string{}, cfg.ServiceAddresses...)
-	rand.Shuffle(len(cfg.ServiceAddresses), func(i, j int) {
+	addresses := append([]string{}, targets...)
+	rand.Shuffle(len(addresses), func(i, j int) {
 		addresses[i], addresses[j] = addresses[j], addresses[i]
 	})
 	for _, addr := range addresses {
diff --git a/pkg/logservice/service.go b/pkg/logservice/service.go
index ab4de7e3d..e84629775 100644
--- a/pkg/logservice/service.go
+++ b/pkg/logservice/service.go
@@ -24,6 +24,7 @@ import (
 	"time"
 
 	"github.com/fagongzi/goetty/v2"
+	"github.com/lni/dragonboat/v4"
 	"github.com/lni/dragonboat/v4/logger"
 
 	"github.com/matrixorigin/matrixone/pkg/common/morpc"
@@ -199,6 +200,8 @@ func (s *Service) handle(req pb.Request,
 		return s.handleCheckHAKeeper(req), pb.LogRecordResponse{}
 	case pb.GET_CLUSTER_DETAILS:
 		return s.handleGetClusterDetails(req), pb.LogRecordResponse{}
+	case pb.GET_SHARD_INFO:
+		return s.handleGetShardInfo(req), pb.LogRecordResponse{}
 	default:
 		panic("unknown log service method type")
 	}
@@ -208,6 +211,16 @@ func getResponse(req pb.Request) pb.Response {
 	return pb.Response{Method: req.Method}
 }
 
+func (s *Service) handleGetShardInfo(req pb.Request) pb.Response {
+	resp := getResponse(req)
+	if result, ok := s.getShardInfo(req.LogRequest.ShardID); !ok {
+		resp.ErrorCode, resp.ErrorMessage = toErrorCode(dragonboat.ErrShardNotFound)
+	} else {
+		resp.ShardInfo = &result
+	}
+	return resp
+}
+
 func (s *Service) handleGetClusterDetails(req pb.Request) pb.Response {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.Timeout))
 	defer cancel()
diff --git a/pkg/logservice/shardinfo.go b/pkg/logservice/shardinfo.go
index 9ff374047..d2d2dbc83 100644
--- a/pkg/logservice/shardinfo.go
+++ b/pkg/logservice/shardinfo.go
@@ -15,46 +15,115 @@
 package logservice
 
 import (
+	"context"
+	"reflect"
+	"sync"
+	"time"
+
 	"github.com/matrixorigin/matrixone/pkg/common/moerr"
+	"github.com/matrixorigin/matrixone/pkg/common/morpc"
+	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
 )
 
-type ReplicaInfo struct {
-	UUID           string
-	ServiceAddress string
+type ShardInfo struct {
+	// ReplicaID is the ID of the replica recommended to use
+	ReplicaID uint64
+	// Replicas is a map of replica ID to their service addresses
+	Replicas map[uint64]string
 }
 
-type ShardInfo struct {
-	ShardID  uint64
-	Replicas map[uint64]ReplicaInfo
-	Epoch    uint64
-	LeaderID uint64
-	Term     uint64
+// GetShardInfo is to be invoked when querying ShardInfo on a Log Service node.
+// address is usually the reverse proxy that randomly redirect the request to
+// a known Log Service node.
+func GetShardInfo(address string, shardID uint64) (ShardInfo, bool, error) {
+	timeout := time.Second
+	respPool := &sync.Pool{}
+	respPool.New = func() interface{} {
+		return &RPCResponse{pool: respPool}
+	}
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+	cc, err := getRPCClient(ctx, address, respPool)
+	if err != nil {
+		return ShardInfo{}, false, err
+	}
+	defer func() {
+		if err := cc.Close(); err != nil {
+			plog.Errorf("failed to close client %v", err)
+		}
+	}()
+	req := pb.Request{
+		Method:  pb.GET_SHARD_INFO,
+		Timeout: int64(timeout),
+		LogRequest: pb.LogRequest{
+			ShardID: shardID,
+		},
+	}
+	rpcReq := &RPCRequest{
+		Request: req,
+	}
+	future, err := cc.Send(ctx,
+		address, rpcReq, morpc.SendOptions{Timeout: time.Duration(timeout)})
+	if err != nil {
+		return ShardInfo{}, false, err
+	}
+	defer future.Close()
+	msg, err := future.Get()
+	if err != nil {
+		return ShardInfo{}, false, err
+	}
+	response, ok := msg.(*RPCResponse)
+	if !ok {
+		panic("unexpected response type")
+	}
+	resp := response.Response
+	defer response.Release()
+	err = toError(resp)
+	if err != nil {
+		return ShardInfo{}, false, err
+	}
+	si := *resp.ShardInfo
+	if reflect.DeepEqual(si, pb.ShardInfoQueryResult{}) {
+		return ShardInfo{}, false, nil
+	}
+	// leader address is unknown
+	if _, ok := si.Replicas[si.LeaderID]; !ok {
+		return ShardInfo{}, false, nil
+	}
+	result := ShardInfo{
+		ReplicaID: si.LeaderID,
+		Replicas:  make(map[uint64]string),
+	}
+	for replicaID, info := range si.Replicas {
+		result.Replicas[replicaID] = info.ServiceAddress
+	}
+	return result, true, nil
 }
 
-func (s *Service) GetShardInfo(shardID uint64) (ShardInfo, bool) {
+func (s *Service) getShardInfo(shardID uint64) (pb.ShardInfoQueryResult, bool) {
 	r, ok := s.store.nh.GetNodeHostRegistry()
 	if !ok {
 		panic(moerr.NewError(moerr.INVALID_STATE, "gossip registry not enabled"))
 	}
 	shard, ok := r.GetShardInfo(shardID)
 	if !ok {
-		return ShardInfo{}, false
+		return pb.ShardInfoQueryResult{}, false
 	}
-	result := ShardInfo{
+	result := pb.ShardInfoQueryResult{
 		ShardID:  shard.ShardID,
 		Epoch:    shard.ConfigChangeIndex,
 		LeaderID: shard.LeaderID,
 		Term:     shard.Term,
-		Replicas: make(map[uint64]ReplicaInfo),
+		Replicas: make(map[uint64]pb.ReplicaInfo),
 	}
 	for nodeID, uuid := range shard.Nodes {
 		data, ok := r.GetMeta(uuid)
 		if !ok {
-			return ShardInfo{}, false
+			return pb.ShardInfoQueryResult{}, false
 		}
 		var md storeMeta
 		md.unmarshal(data)
-		result.Replicas[nodeID] = ReplicaInfo{
+		result.Replicas[nodeID] = pb.ReplicaInfo{
 			UUID:           uuid,
 			ServiceAddress: md.serviceAddress,
 		}
diff --git a/pkg/pb/logservice/logservice.pb.go b/pkg/pb/logservice/logservice.pb.go
index 58d10af1c..71f4184e2 100644
--- a/pkg/pb/logservice/logservice.pb.go
+++ b/pkg/pb/logservice/logservice.pb.go
@@ -96,6 +96,7 @@ const (
 	DN_HEARTBEAT        MethodType = 9
 	CHECK_HAKEEPER      MethodType = 10
 	GET_CLUSTER_DETAILS MethodType = 11
+	GET_SHARD_INFO      MethodType = 12
 )
 
 var MethodType_name = map[int32]string{
@@ -111,6 +112,7 @@ var MethodType_name = map[int32]string{
 	9:  "DN_HEARTBEAT",
 	10: "CHECK_HAKEEPER",
 	11: "GET_CLUSTER_DETAILS",
+	12: "GET_SHARD_INFO",
 }
 
 var MethodType_value = map[string]int32{
@@ -126,6 +128,7 @@ var MethodType_value = map[string]int32{
 	"DN_HEARTBEAT":        9,
 	"CHECK_HAKEEPER":      10,
 	"GET_CLUSTER_DETAILS": 11,
+	"GET_SHARD_INFO":      12,
 }
 
 func (x MethodType) String() string {
@@ -1444,18 +1447,19 @@ func (m *LogResponse) GetLastLsn() uint64 {
 }
 
 type Response struct {
-	RequestID            uint64         `protobuf:"varint,1,opt,name=RequestID,proto3" json:"RequestID,omitempty"`
-	Method               MethodType     `protobuf:"varint,2,opt,name=Method,proto3,enum=logservice.MethodType" json:"Method,omitempty"`
-	ErrorCode            ErrorCode      `protobuf:"varint,3,opt,name=ErrorCode,proto3,enum=logservice.ErrorCode" json:"ErrorCode,omitempty"`
-	ErrorMessage         string         `protobuf:"bytes,4,opt,name=ErrorMessage,proto3" json:"ErrorMessage,omitempty"`
-	IsHAKeeper           bool           `protobuf:"varint,5,opt,name=IsHAKeeper,proto3" json:"IsHAKeeper,omitempty"`
-	LogResponse          LogResponse    `protobuf:"bytes,6,opt,name=LogResponse,proto3" json:"LogResponse"`
-	CommandBatch         CommandBatch   `protobuf:"bytes,7,opt,name=CommandBatch,proto3" json:"CommandBatch"`
-	TsoResponse          TsoResponse    `protobuf:"bytes,8,opt,name=TsoResponse,proto3" json:"TsoResponse"`
-	ClusterDetails       ClusterDetails `protobuf:"bytes,9,opt,name=ClusterDetails,proto3" json:"ClusterDetails"`
-	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
-	XXX_unrecognized     []byte         `json:"-"`
-	XXX_sizecache        int32          `json:"-"`
+	RequestID            uint64                `protobuf:"varint,1,opt,name=RequestID,proto3" json:"RequestID,omitempty"`
+	Method               MethodType            `protobuf:"varint,2,opt,name=Method,proto3,enum=logservice.MethodType" json:"Method,omitempty"`
+	ErrorCode            ErrorCode             `protobuf:"varint,3,opt,name=ErrorCode,proto3,enum=logservice.ErrorCode" json:"ErrorCode,omitempty"`
+	ErrorMessage         string                `protobuf:"bytes,4,opt,name=ErrorMessage,proto3" json:"ErrorMessage,omitempty"`
+	IsHAKeeper           bool                  `protobuf:"varint,5,opt,name=IsHAKeeper,proto3" json:"IsHAKeeper,omitempty"`
+	LogResponse          LogResponse           `protobuf:"bytes,6,opt,name=LogResponse,proto3" json:"LogResponse"`
+	CommandBatch         CommandBatch          `protobuf:"bytes,7,opt,name=CommandBatch,proto3" json:"CommandBatch"`
+	TsoResponse          TsoResponse           `protobuf:"bytes,8,opt,name=TsoResponse,proto3" json:"TsoResponse"`
+	ClusterDetails       ClusterDetails        `protobuf:"bytes,9,opt,name=ClusterDetails,proto3" json:"ClusterDetails"`
+	ShardInfo            *ShardInfoQueryResult `protobuf:"bytes,10,opt,name=ShardInfo,proto3" json:"ShardInfo,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
 }
 
 func (m *Response) Reset()         { *m = Response{} }
@@ -1554,6 +1558,13 @@ func (m *Response) GetClusterDetails() ClusterDetails {
 	return ClusterDetails{}
 }
 
+func (m *Response) GetShardInfo() *ShardInfoQueryResult {
+	if m != nil {
+		return m.ShardInfo
+	}
+	return nil
+}
+
 type LogRecordResponse struct {
 	Records              []LogRecord `protobuf:"bytes,1,rep,name=Records,proto3" json:"Records"`
 	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
@@ -2724,6 +2735,142 @@ func (m *HAKeeperRSMState) GetClusterInfo() ClusterInfo {
 	return ClusterInfo{}
 }
 
+// ReplicaInfo contains details of a replica
+type ReplicaInfo struct {
+	UUID                 string   `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"`
+	ServiceAddress       string   `protobuf:"bytes,2,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+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{34}
+}
+func (m *ReplicaInfo) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ReplicaInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ReplicaInfo.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 *ReplicaInfo) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ReplicaInfo.Merge(m, src)
+}
+func (m *ReplicaInfo) XXX_Size() int {
+	return m.Size()
+}
+func (m *ReplicaInfo) XXX_DiscardUnknown() {
+	xxx_messageInfo_ReplicaInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ReplicaInfo proto.InternalMessageInfo
+
+func (m *ReplicaInfo) GetUUID() string {
+	if m != nil {
+		return m.UUID
+	}
+	return ""
+}
+
+func (m *ReplicaInfo) GetServiceAddress() string {
+	if m != nil {
+		return m.ServiceAddress
+	}
+	return ""
+}
+
+// ShardInfoQueryResult contains the result of the shard info query.
+type ShardInfoQueryResult struct {
+	ShardID              uint64                 `protobuf:"varint,1,opt,name=ShardID,proto3" json:"ShardID,omitempty"`
+	Replicas             map[uint64]ReplicaInfo `protobuf:"bytes,2,rep,name=Replicas,proto3" json:"Replicas" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	Epoch                uint64                 `protobuf:"varint,3,opt,name=Epoch,proto3" json:"Epoch,omitempty"`
+	LeaderID             uint64                 `protobuf:"varint,4,opt,name=LeaderID,proto3" json:"LeaderID,omitempty"`
+	Term                 uint64                 `protobuf:"varint,5,opt,name=Term,proto3" json:"Term,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}               `json:"-"`
+	XXX_unrecognized     []byte                 `json:"-"`
+	XXX_sizecache        int32                  `json:"-"`
+}
+
+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{35}
+}
+func (m *ShardInfoQueryResult) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ShardInfoQueryResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ShardInfoQueryResult.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 *ShardInfoQueryResult) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ShardInfoQueryResult.Merge(m, src)
+}
+func (m *ShardInfoQueryResult) XXX_Size() int {
+	return m.Size()
+}
+func (m *ShardInfoQueryResult) XXX_DiscardUnknown() {
+	xxx_messageInfo_ShardInfoQueryResult.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ShardInfoQueryResult proto.InternalMessageInfo
+
+func (m *ShardInfoQueryResult) GetShardID() uint64 {
+	if m != nil {
+		return m.ShardID
+	}
+	return 0
+}
+
+func (m *ShardInfoQueryResult) GetReplicas() map[uint64]ReplicaInfo {
+	if m != nil {
+		return m.Replicas
+	}
+	return nil
+}
+
+func (m *ShardInfoQueryResult) GetEpoch() uint64 {
+	if m != nil {
+		return m.Epoch
+	}
+	return 0
+}
+
+func (m *ShardInfoQueryResult) GetLeaderID() uint64 {
+	if m != nil {
+		return m.LeaderID
+	}
+	return 0
+}
+
+func (m *ShardInfoQueryResult) GetTerm() uint64 {
+	if m != nil {
+		return m.Term
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterEnum("logservice.UpdateType", UpdateType_name, UpdateType_value)
 	proto.RegisterEnum("logservice.NodeState", NodeState_name, NodeState_value)
@@ -2777,164 +2924,173 @@ func init() {
 	proto.RegisterType((*HAKeeperRSMState)(nil), "logservice.HAKeeperRSMState")
 	proto.RegisterMapType((map[string]uint64)(nil), "logservice.HAKeeperRSMState.LogShardsEntry")
 	proto.RegisterMapType((map[string]CommandBatch)(nil), "logservice.HAKeeperRSMState.ScheduleCommandsEntry")
+	proto.RegisterType((*ReplicaInfo)(nil), "logservice.ReplicaInfo")
+	proto.RegisterType((*ShardInfoQueryResult)(nil), "logservice.ShardInfoQueryResult")
+	proto.RegisterMapType((map[uint64]ReplicaInfo)(nil), "logservice.ShardInfoQueryResult.ReplicasEntry")
 }
 
 func init() { proto.RegisterFile("logservice.proto", fileDescriptor_fd1040c5381ab5a7) }
 
 var fileDescriptor_fd1040c5381ab5a7 = []byte{
-	// 2427 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0x4f, 0x6f, 0x1b, 0xc7,
-	0x15, 0xd7, 0x92, 0x14, 0xff, 0x3c, 0x4a, 0xf4, 0x6a, 0x64, 0xd9, 0x8c, 0x92, 0xca, 0xea, 0x36,
-	0x35, 0x5c, 0x26, 0xa1, 0x00, 0x19, 0x2e, 0x9a, 0x56, 0x89, 0x41, 0x71, 0x69, 0x8b, 0x31, 0xbd,
-	0x32, 0x96, 0x54, 0x0f, 0x01, 0x02, 0x75, 0xc5, 0x1d, 0x91, 0x5b, 0x93, 0xbb, 0xec, 0xee, 0x52,
-	0xb5, 0x72, 0x6a, 0x0f, 0x2e, 0x50, 0xf4, 0xd4, 0x5b, 0x50, 0xf4, 0x3b, 0xf4, 0xd0, 0x7c, 0x82,
-	0x22, 0x87, 0x1c, 0x0d, 0x14, 0xbd, 0x06, 0x8d, 0x4f, 0xbd, 0xf5, 0x2b, 0x14, 0xf3, 0x6f, 0x77,
-	0x86, 0xa4, 0x64, 0x3b, 0x71, 0xd1, 0xe6, 0xb6, 0xf3, 0xfe, 0xcd, 0x9b, 0x37, 0xbf, 0xf7, 0xde,
-	0xcc, 0x2c, 0xe8, 0xa3, 0x60, 0x10, 0xe1, 0xf0, 0xcc, 0xeb, 0xe3, 0xfa, 0x24, 0x0c, 0xe2, 0x00,
-	0x41, 0x4a, 0xd9, 0x7c, 0x6f, 0xe0, 0xc5, 0xc3, 0xe9, 0x49, 0xbd, 0x1f, 0x8c, 0x77, 0x06, 0xc1,
-	0x20, 0xd8, 0xa1, 0x22, 0x27, 0xd3, 0x53, 0x3a, 0xa2, 0x03, 0xfa, 0xc5, 0x54, 0x37, 0x2b, 0x63,
-	0x1c, 0x3b, 0xae, 0x13, 0x3b, 0x6c, 0x6c, 0x3c, 0xd5, 0x20, 0xdf, 0xb4, 0xac, 0xc0, 0xc5, 0x08,
-	0x41, 0xee, 0xe8, 0xa8, 0x6d, 0x56, 0xb5, 0x6d, 0xed, 0x56, 0xc9, 0xa6, 0xdf, 0xe8, 0x26, 0x54,
-	0xba, 0x6c, 0xa2, 0x86, 0xeb, 0x86, 0x38, 0x8a, 0xaa, 0x19, 0xca, 0x9d, 0xa1, 0x12, 0xdd, 0x9e,
-	0xd7, 0x7f, 0x5c, 0xcd, 0x6e, 0x6b, 0xb7, 0x72, 0x36, 0xfd, 0x46, 0xef, 0xc0, 0x72, 0x37, 0x76,
-	0x62, 0x5c, 0xcd, 0x6d, 0x6b, 0xb7, 0x2a, 0xbb, 0x1b, 0x75, 0x69, 0x1d, 0x64, 0x42, 0xca, 0xb4,
-	0x99, 0x0c, 0xf5, 0xc3, 0xfc, 0x3f, 0xf0, 0xe3, 0x0b, 0x0d, 0x0a, 0x9d, 0x60, 0xf0, 0x3f, 0x77,
-	0x04, 0xed, 0x41, 0xd1, 0xc6, 0x93, 0x91, 0xd7, 0x77, 0xa2, 0xea, 0xf2, 0x76, 0xf6, 0x56, 0x79,
-	0x77, 0x53, 0x96, 0xef, 0x04, 0x03, 0xce, 0x6e, 0xfb, 0xa7, 0xc1, 0x7e, 0xee, 0xcb, 0xaf, 0x6e,
-	0x2c, 0xd9, 0x89, 0x86, 0xf1, 0x6f, 0x0d, 0x56, 0x3a, 0xc1, 0xa0, 0x3b, 0x74, 0x42, 0x97, 0x08,
-	0xa0, 0x2a, 0x14, 0xd8, 0x80, 0x2d, 0x27, 0x67, 0x8b, 0x21, 0xda, 0x97, 0x26, 0xca, 0xd0, 0x89,
-	0x6e, 0xce, 0x4c, 0x94, 0x58, 0xa9, 0x0b, 0xc1, 0x96, 0x1f, 0x87, 0xe7, 0xe9, 0x74, 0xe8, 0x2a,
-	0x2c, 0xb7, 0x26, 0x41, 0x7f, 0xc8, 0x97, 0xcb, 0x06, 0x68, 0x13, 0x8a, 0x1d, 0xec, 0xb8, 0x38,
-	0x6c, 0x9b, 0x74, 0xc9, 0x39, 0x3b, 0x19, 0xd3, 0xf8, 0xe0, 0x70, 0x5c, 0x5d, 0xe6, 0xf1, 0xc1,
-	0xe1, 0x78, 0xf3, 0x67, 0xb0, 0xaa, 0x4c, 0x80, 0x74, 0xc8, 0x3e, 0xc6, 0xe7, 0xdc, 0x61, 0xf2,
-	0x49, 0x26, 0x3a, 0x73, 0x46, 0x53, 0xcc, 0xa3, 0xce, 0x06, 0x3f, 0xcd, 0xfc, 0x44, 0x33, 0xce,
-	0xa0, 0xa2, 0xc6, 0x04, 0xdd, 0x53, 0x43, 0x40, 0xcd, 0x94, 0x77, 0xab, 0x17, 0x2d, 0x6e, 0xbf,
-	0x48, 0x62, 0xf8, 0xec, 0xab, 0x1b, 0x9a, 0xad, 0x86, 0xee, 0x2d, 0x28, 0x09, 0xb3, 0x26, 0x9d,
-	0x37, 0x67, 0xa7, 0x04, 0xc3, 0x02, 0xbd, 0x69, 0x75, 0xe3, 0x20, 0xc4, 0x07, 0xd8, 0x09, 0xe3,
-	0x13, 0xec, 0xc4, 0xdf, 0x06, 0x38, 0xc6, 0x3f, 0x34, 0x58, 0x23, 0xd3, 0xbf, 0xd8, 0xe2, 0x36,
-	0x94, 0x6d, 0xe7, 0x34, 0x56, 0xcd, 0xc9, 0xa4, 0x05, 0x73, 0x66, 0x17, 0x82, 0xf5, 0x6d, 0x58,
-	0xbd, 0x1f, 0x44, 0x91, 0x37, 0x11, 0x62, 0x39, 0x2a, 0xa6, 0x12, 0xbf, 0x25, 0x22, 0x5b, 0x50,
-	0x36, 0xad, 0x97, 0xc1, 0xe3, 0xe5, 0xe1, 0x7e, 0xaa, 0x81, 0x6e, 0xbe, 0xc6, 0x78, 0xa3, 0x3b,
-	0x90, 0xa7, 0x33, 0x93, 0xd8, 0x90, 0x35, 0x5d, 0x97, 0xd7, 0x24, 0x79, 0xcc, 0x17, 0xc4, 0x85,
-	0x8d, 0xdf, 0x67, 0xa0, 0x68, 0x77, 0x1f, 0xb2, 0x5c, 0xd5, 0x21, 0xdb, 0x8b, 0x02, 0x81, 0xd3,
-	0x5e, 0x14, 0x10, 0x9c, 0xb6, 0x7d, 0x17, 0x3f, 0xe1, 0x0b, 0x60, 0x03, 0x12, 0xe7, 0x0e, 0x76,
-	0x22, 0x7c, 0x10, 0x8c, 0x58, 0x56, 0xb0, 0x74, 0x51, 0x89, 0xc8, 0x80, 0x95, 0x5e, 0x38, 0xf5,
-	0xfb, 0x4e, 0x8c, 0xdd, 0x4e, 0xe4, 0xf3, 0xd4, 0x51, 0x68, 0xe8, 0x23, 0x58, 0x61, 0x4a, 0x5e,
-	0x14, 0x07, 0xe1, 0x39, 0xdf, 0x0f, 0x25, 0x71, 0x85, 0x77, 0x75, 0x59, 0x90, 0x25, 0xae, 0xa2,
-	0xbb, 0x79, 0x17, 0xd6, 0xe6, 0x44, 0x5e, 0x94, 0x7a, 0x39, 0x39, 0xf5, 0x3e, 0x81, 0x12, 0xdd,
-	0xfc, 0x7e, 0x10, 0xba, 0x44, 0x91, 0x38, 0xcd, 0x15, 0x89, 0xaf, 0x35, 0xc8, 0xf5, 0xce, 0x27,
-	0x4c, 0xaf, 0xb2, 0x7b, 0x4d, 0xf1, 0x91, 0xea, 0x10, 0xae, 0x4d, 0x65, 0xc8, 0x4e, 0x9a, 0x4e,
-	0xec, 0xd0, 0xc0, 0xac, 0xd8, 0xf4, 0xdb, 0xf8, 0x4c, 0x03, 0xa0, 0xf6, 0x7f, 0x35, 0xc5, 0x11,
-	0xdd, 0x6c, 0xcb, 0x19, 0x63, 0xb1, 0xd9, 0xe4, 0x5b, 0x46, 0x53, 0x46, 0x45, 0x13, 0x77, 0x27,
-	0x9b, 0xba, 0x53, 0x85, 0xc2, 0x43, 0xe7, 0x49, 0xd7, 0xfb, 0x14, 0xf3, 0xc8, 0x8a, 0x21, 0x41,
-	0x9e, 0xd8, 0x70, 0x93, 0x17, 0xa6, 0x94, 0x40, 0x5d, 0xb3, 0xda, 0x66, 0x35, 0xcf, 0x2a, 0x16,
-	0xf9, 0x36, 0x0c, 0x80, 0x5e, 0x14, 0x08, 0xcf, 0xae, 0xc2, 0x72, 0x33, 0x98, 0xfa, 0x31, 0x5f,
-	0x3c, 0x1b, 0x18, 0x7f, 0xcb, 0x42, 0x41, 0x48, 0x50, 0x6c, 0xd3, 0xcf, 0x04, 0xf7, 0x29, 0x01,
-	0xd5, 0x21, 0xff, 0x10, 0xc7, 0xc3, 0xc0, 0x5d, 0x14, 0x2a, 0xc6, 0xa1, 0xa1, 0xe2, 0x52, 0x64,
-	0x25, 0x3d, 0x6f, 0x8c, 0x83, 0x69, 0x4c, 0xd7, 0x97, 0xb5, 0xc5, 0x10, 0xed, 0xc9, 0x11, 0xa3,
-	0xcb, 0x2c, 0xab, 0xd6, 0x52, 0x2e, 0xc7, 0xb5, 0x1c, 0xe1, 0xfb, 0xb4, 0x70, 0x26, 0xe9, 0x45,
-	0x43, 0x51, 0xde, 0xfd, 0xde, 0x6c, 0xe1, 0x54, 0x72, 0x90, 0x9b, 0x51, 0x14, 0x91, 0x09, 0xe5,
-	0xa6, 0x95, 0xda, 0xc9, 0x53, 0x3b, 0x6f, 0xc9, 0x76, 0x66, 0x4b, 0x27, 0x37, 0x23, 0xab, 0x11,
-	0x2b, 0xa6, 0x64, 0xa5, 0x30, 0x6f, 0xc5, 0xbc, 0xc0, 0x8a, 0xa4, 0x46, 0x42, 0x92, 0x6e, 0x55,
-	0xb5, 0x38, 0x1f, 0x92, 0x94, 0x2b, 0x42, 0x92, 0x52, 0x8c, 0x2e, 0x94, 0x69, 0x80, 0xa2, 0x49,
-	0xe0, 0x47, 0xf8, 0x92, 0xea, 0xc5, 0xf1, 0x96, 0x51, 0xf0, 0xd6, 0x71, 0xa2, 0x38, 0x45, 0xa1,
-	0x18, 0x1a, 0x5f, 0x67, 0x49, 0x45, 0xe5, 0x26, 0x5f, 0x2f, 0x34, 0x6e, 0x43, 0xa9, 0x15, 0x86,
-	0x41, 0xd8, 0x0c, 0x5c, 0x4c, 0xa7, 0x9d, 0x39, 0x6e, 0x24, 0x4c, 0x3b, 0x95, 0x23, 0x85, 0x87,
-	0x0e, 0x1e, 0xe2, 0x28, 0x72, 0x06, 0x98, 0x77, 0x01, 0x85, 0x86, 0xb6, 0x00, 0xda, 0xd1, 0x41,
-	0xe3, 0x01, 0xc6, 0x13, 0x1c, 0x52, 0x64, 0x14, 0x6d, 0x89, 0x82, 0xee, 0x2a, 0x81, 0xe2, 0x5b,
-	0x7e, 0x7d, 0x0e, 0x7a, 0x8c, 0x2d, 0xf6, 0x49, 0x0e, 0xed, 0x3e, 0xac, 0x34, 0x83, 0xf1, 0xd8,
-	0xf1, 0xdd, 0x7d, 0x27, 0xee, 0x0f, 0xf9, 0x76, 0x2b, 0x5d, 0x5b, 0xe6, 0x0b, 0xdc, 0xc9, 0x34,
-	0xe2, 0x04, 0xdd, 0x3b, 0xee, 0x44, 0x71, 0xde, 0x09, 0x89, 0x2d, 0x9c, 0x90, 0x48, 0xe8, 0x00,
-	0x2a, 0xcd, 0xd1, 0x34, 0x8a, 0x71, 0x68, 0xe2, 0xd8, 0xf1, 0x46, 0x51, 0xb5, 0x44, 0x6d, 0x28,
-	0x0d, 0x4f, 0x95, 0xe0, 0x66, 0x66, 0xf4, 0x8c, 0x8f, 0x68, 0x37, 0x67, 0x75, 0x2e, 0x31, 0x7f,
-	0x87, 0x54, 0x04, 0x42, 0x89, 0xaa, 0x1a, 0x2d, 0xdc, 0x1b, 0x73, 0x01, 0x22, 0x5c, 0x6e, 0x52,
-	0xc8, 0x1a, 0x3f, 0x50, 0x96, 0x45, 0xca, 0xcd, 0xcf, 0x69, 0x41, 0xe6, 0xe5, 0x86, 0x0e, 0x8c,
-	0x3f, 0x68, 0xc4, 0x38, 0x6d, 0x97, 0x0b, 0xfb, 0xe2, 0xc5, 0xa5, 0x52, 0x69, 0xbc, 0xd9, 0x99,
-	0xc6, 0x9b, 0x1e, 0xf1, 0x72, 0xf2, 0x11, 0x6f, 0x8b, 0x16, 0x1a, 0xb5, 0x66, 0x4a, 0x14, 0xe3,
-	0x4f, 0x19, 0xb2, 0x9d, 0xfe, 0xa9, 0x37, 0x68, 0x0e, 0x1d, 0x7f, 0x80, 0xd1, 0xed, 0xc4, 0x3b,
-	0x7e, 0x1e, 0x5b, 0x57, 0xfb, 0x01, 0x65, 0xa5, 0x0b, 0x67, 0xeb, 0xd8, 0x03, 0x60, 0xea, 0x52,
-	0x1f, 0x51, 0xcb, 0x88, 0x34, 0x05, 0xcd, 0x03, 0x49, 0x1e, 0xf5, 0xa0, 0xd2, 0xf6, 0xbd, 0xd8,
-	0x73, 0x46, 0x0f, 0xf1, 0xf8, 0x04, 0x87, 0xa2, 0xd3, 0xbf, 0x7b, 0x91, 0x85, 0xba, 0x2a, 0xce,
-	0x7a, 0xe6, 0x8c, 0x8d, 0xcd, 0x06, 0xac, 0x2f, 0x10, 0x7b, 0xa5, 0x23, 0xeb, 0x8f, 0x60, 0xb5,
-	0x3b, 0x9c, 0xc6, 0x6e, 0xf0, 0x6b, 0x9f, 0xd6, 0x2f, 0xba, 0x37, 0xe4, 0x23, 0xd9, 0x32, 0x31,
-	0x34, 0x9e, 0x66, 0xe0, 0x4a, 0xb7, 0x3f, 0xc4, 0xee, 0x74, 0x84, 0x39, 0xd4, 0x17, 0xee, 0xee,
-	0xdb, 0xb0, 0xba, 0x1f, 0x04, 0x71, 0x14, 0x87, 0xce, 0x64, 0xe2, 0xf9, 0x03, 0x3a, 0x69, 0xd1,
-	0x56, 0x89, 0x68, 0x4f, 0xdd, 0x14, 0xba, 0xd9, 0x73, 0x39, 0x96, 0xf2, 0x6d, 0x75, 0x0b, 0xdf,
-	0x87, 0x32, 0x3f, 0x43, 0xd1, 0xed, 0x60, 0x97, 0x19, 0x25, 0xbb, 0x24, 0xb6, 0x2d, 0xcb, 0xa2,
-	0xbb, 0x33, 0x2b, 0xe6, 0xad, 0xe5, 0x0d, 0x45, 0x59, 0x16, 0xb0, 0x55, 0x79, 0xc3, 0x51, 0xab,
-	0x43, 0x72, 0x8d, 0xd0, 0xd2, 0x6b, 0x04, 0xfa, 0x00, 0x8a, 0x5c, 0x46, 0x5c, 0x68, 0xde, 0x54,
-	0xec, 0xab, 0x61, 0x14, 0x07, 0x55, 0xa1, 0x62, 0xb4, 0x49, 0xd3, 0x62, 0x71, 0x27, 0x07, 0x55,
-	0x71, 0x91, 0xd3, 0xa4, 0x8b, 0xdc, 0xcb, 0x9e, 0xe5, 0xff, 0xac, 0x41, 0x81, 0xd8, 0x22, 0x67,
-	0xc4, 0x0f, 0x20, 0x4f, 0x8d, 0x8a, 0x94, 0xbf, 0x31, 0xdb, 0x06, 0xc9, 0x51, 0x8d, 0x49, 0x50,
-	0x24, 0x25, 0xe7, 0x4d, 0x4a, 0xda, 0xb4, 0xa1, 0x2c, 0x31, 0x65, 0x98, 0x95, 0x18, 0xcc, 0xde,
-	0x93, 0x61, 0x36, 0x53, 0xed, 0xa4, 0xf5, 0xc8, 0xf8, 0xfb, 0x8d, 0x46, 0xcf, 0xe4, 0xaf, 0x63,
-	0xa9, 0xdf, 0xf4, 0x18, 0x4d, 0x22, 0x64, 0xbe, 0x4c, 0x84, 0xcc, 0xff, 0x6e, 0x84, 0xcc, 0xc5,
-	0x11, 0xfa, 0x8b, 0x36, 0xdb, 0x08, 0xd0, 0x2e, 0x71, 0x98, 0xdc, 0xd6, 0x85, 0x9b, 0x48, 0xb5,
-	0x43, 0x58, 0xa2, 0x7e, 0x71, 0x41, 0xa2, 0xd3, 0xe4, 0x3a, 0x99, 0x79, 0x9d, 0xa6, 0xa2, 0xc3,
-	0x05, 0xd1, 0x1d, 0x28, 0xf2, 0x77, 0x08, 0x11, 0xd2, 0xf5, 0x99, 0x26, 0x21, 0x69, 0x25, 0xa2,
-	0xc6, 0xef, 0x34, 0x28, 0x73, 0x8f, 0xe9, 0x9e, 0xbe, 0x0f, 0x45, 0x1e, 0x7d, 0xe1, 0xef, 0xf5,
-	0x7a, 0xf2, 0x04, 0xc4, 0x39, 0x4a, 0xb7, 0x49, 0xc4, 0xd1, 0x1e, 0x3d, 0xd6, 0x73, 0x5d, 0xe6,
-	0x77, 0x35, 0xd5, 0x15, 0x2c, 0x45, 0x39, 0x55, 0x30, 0xfe, 0xa8, 0xc1, 0x06, 0x2f, 0x90, 0xdc,
-	0x1f, 0x71, 0xbc, 0xbc, 0x09, 0x15, 0x6b, 0x3a, 0x3e, 0x3c, 0x4d, 0x8d, 0x33, 0xc0, 0xcd, 0x50,
-	0x49, 0x2d, 0xa3, 0x94, 0xc4, 0x7f, 0xd6, 0xaf, 0x54, 0x22, 0xaa, 0x81, 0x2e, 0xf4, 0x92, 0xdb,
-	0x29, 0x6b, 0x5e, 0x73, 0x74, 0xe3, 0x19, 0x7f, 0x15, 0xb9, 0x14, 0xf1, 0xdf, 0xad, 0x6b, 0xf5,
-	0xe7, 0x19, 0x8a, 0x13, 0x96, 0x41, 0x1f, 0x26, 0x49, 0xc8, 0xb6, 0x7a, 0x7b, 0xee, 0xc8, 0x4e,
-	0x53, 0x88, 0x8a, 0xa8, 0x29, 0xc4, 0x62, 0xf9, 0x61, 0x92, 0x81, 0x99, 0xcb, 0xf4, 0x2f, 0x4c,
-	0xc1, 0x2e, 0x94, 0x25, 0xe3, 0x0b, 0x7a, 0x61, 0x5d, 0x4d, 0xc1, 0x0b, 0xdf, 0x62, 0xa4, 0x1c,
-	0xa4, 0x46, 0x2f, 0xcd, 0xeb, 0x17, 0x19, 0x5d, 0x94, 0xd8, 0xbf, 0x25, 0xe7, 0x92, 0x21, 0xee,
-	0x3f, 0xc6, 0x21, 0x0b, 0xdd, 0x22, 0x24, 0xdc, 0x55, 0x52, 0x69, 0x61, 0x61, 0x4d, 0xd9, 0xc9,
-	0xcd, 0x45, 0x4a, 0xbe, 0xdb, 0x49, 0x71, 0xe3, 0x2d, 0x76, 0x7d, 0x41, 0x49, 0x4b, 0x8b, 0x05,
-	0xf3, 0xe4, 0xc7, 0xe9, 0x86, 0xf2, 0x9b, 0xdb, 0xd5, 0x45, 0xdb, 0x20, 0x65, 0x3e, 0xd3, 0xdb,
-	0x11, 0xaf, 0x8b, 0xcb, 0xb4, 0x21, 0x2b, 0x3d, 0x55, 0x1c, 0xcf, 0x95, 0xa7, 0xce, 0xcf, 0x97,
-	0x41, 0x17, 0x8c, 0xe4, 0x29, 0x63, 0x51, 0x1c, 0xae, 0x41, 0xde, 0xc2, 0x4f, 0xe2, 0xe4, 0xc4,
-	0xc8, 0x47, 0x49, 0xf3, 0xcd, 0x4a, 0xcd, 0x77, 0x47, 0x7d, 0xe3, 0x7c, 0xa1, 0x17, 0xc8, 0x05,
-	0x7d, 0xa6, 0x23, 0x8b, 0x34, 0xd8, 0x5d, 0xa4, 0x9b, 0xbc, 0x6a, 0xcc, 0x2a, 0xc9, 0x78, 0x9c,
-	0xb3, 0x88, 0xda, 0x72, 0x2d, 0xcb, 0x53, 0xf3, 0xef, 0x5c, 0x6a, 0x3e, 0x91, 0x66, 0xa7, 0xbf,
-	0x54, 0x9b, 0x6c, 0x2a, 0x6f, 0xd7, 0xfc, 0x6e, 0xb2, 0xbe, 0xa0, 0x93, 0xa7, 0xd5, 0x9c, 0xad,
-	0x52, 0x42, 0x42, 0xf1, 0x1b, 0x21, 0xa1, 0xf4, 0x0a, 0x48, 0x98, 0xc1, 0x2d, 0xbc, 0x2a, 0x6e,
-	0x37, 0x3f, 0x81, 0x8d, 0x85, 0xe1, 0x7d, 0xc5, 0xe4, 0x93, 0x4f, 0x6a, 0x72, 0x46, 0xef, 0xd1,
-	0xa7, 0xda, 0x0b, 0x2a, 0x45, 0xe9, 0x05, 0xaf, 0x4d, 0xb5, 0x5f, 0x00, 0x1c, 0x4d, 0x5c, 0x27,
-	0x66, 0x27, 0xca, 0xeb, 0xb0, 0xae, 0xbc, 0x9e, 0x31, 0x96, 0xbe, 0x84, 0x36, 0x60, 0x4d, 0xbc,
-	0x98, 0x75, 0xba, 0x16, 0x27, 0x6b, 0x68, 0x1d, 0xae, 0x1c, 0x45, 0x38, 0xa4, 0xd3, 0x72, 0x62,
-	0x06, 0xad, 0x42, 0xa9, 0xd7, 0x3d, 0xe4, 0xc3, 0x6c, 0xad, 0x0e, 0xa5, 0xe4, 0x39, 0x1e, 0x5d,
-	0x81, 0xb2, 0x15, 0x84, 0x63, 0x67, 0x44, 0x87, 0xfa, 0x12, 0xd2, 0x61, 0x85, 0x3f, 0xb3, 0x30,
-	0x8a, 0x56, 0xfb, 0xbb, 0x06, 0x90, 0xde, 0xc1, 0x51, 0x05, 0xa0, 0xd7, 0x3d, 0x3c, 0x3e, 0x7a,
-	0x64, 0x36, 0x7a, 0x2d, 0x7d, 0x09, 0x01, 0xe4, 0x1b, 0x8f, 0x1e, 0xb5, 0x2c, 0x53, 0xd7, 0x50,
-	0x11, 0x72, 0x76, 0xab, 0x61, 0xea, 0x19, 0xb4, 0x02, 0xc5, 0x9e, 0x7d, 0x64, 0x35, 0x89, 0x4c,
-	0x96, 0x18, 0xbd, 0xdf, 0xea, 0x1d, 0x27, 0x94, 0x1c, 0x2a, 0x43, 0xa1, 0x79, 0x68, 0x59, 0xad,
-	0x66, 0x4f, 0x5f, 0x26, 0x26, 0xf9, 0xe0, 0xd8, 0x3e, 0xd4, 0xf3, 0x68, 0x0d, 0x56, 0x3b, 0x87,
-	0xf7, 0x8f, 0x0f, 0x5a, 0x0d, 0xbb, 0xb7, 0xdf, 0x6a, 0xf4, 0xf4, 0x02, 0xb1, 0xd0, 0xb4, 0x24,
-	0x4a, 0x91, 0x50, 0x4c, 0x99, 0x52, 0x42, 0x08, 0x2a, 0xcd, 0x83, 0x56, 0xf3, 0xc1, 0xf1, 0x41,
-	0xe3, 0x41, 0xab, 0xf5, 0xa8, 0x65, 0xeb, 0x40, 0x02, 0x48, 0x66, 0x6e, 0x76, 0x8e, 0xba, 0xbd,
-	0x96, 0x7d, 0x6c, 0xb6, 0x7a, 0x8d, 0x76, 0xa7, 0xab, 0x97, 0x6b, 0x16, 0x40, 0xfa, 0x3c, 0x47,
-	0x3c, 0x20, 0x71, 0x63, 0x14, 0x7d, 0x89, 0xb8, 0xdf, 0xf6, 0x63, 0x1c, 0xfa, 0xce, 0x48, 0xd7,
-	0x48, 0x90, 0xe8, 0x2e, 0x24, 0x11, 0x5d, 0xe3, 0x2f, 0x9d, 0x36, 0xfe, 0x25, 0xee, 0xc7, 0xd8,
-	0xd5, 0xb3, 0xb5, 0xbf, 0x66, 0xa4, 0x37, 0x09, 0xb2, 0x3c, 0x2b, 0xa0, 0x43, 0x7d, 0x89, 0x0c,
-	0x78, 0x48, 0x75, 0x8d, 0x58, 0x6e, 0x3a, 0x7e, 0x1f, 0x8f, 0xb0, 0xab, 0x67, 0xc8, 0x22, 0xda,
-	0xfe, 0x99, 0x33, 0xf2, 0x5c, 0x8a, 0x17, 0x3d, 0x4b, 0x16, 0xc1, 0x29, 0x42, 0x27, 0x27, 0xd1,
-	0x1e, 0x39, 0xe7, 0xa3, 0xc0, 0x71, 0xf5, 0x65, 0x74, 0x0d, 0x90, 0x4a, 0xeb, 0x7a, 0x9f, 0x62,
-	0x3d, 0x4f, 0xec, 0x27, 0x5e, 0x15, 0x88, 0xa3, 0xd4, 0xb0, 0x15, 0xc4, 0x36, 0x76, 0xdc, 0x73,
-	0x16, 0xb7, 0xee, 0x79, 0x14, 0xe3, 0x71, 0x73, 0x14, 0x44, 0xd8, 0xd5, 0x4b, 0x14, 0x64, 0x91,
-	0xdf, 0x18, 0x85, 0x44, 0x22, 0x79, 0x87, 0xd5, 0x5d, 0x12, 0x95, 0xc3, 0x69, 0x7c, 0x78, 0x6a,
-	0x93, 0x8b, 0x91, 0x4e, 0xaa, 0x67, 0xc5, 0x0a, 0x62, 0x09, 0x90, 0xfa, 0x29, 0x03, 0x50, 0x2c,
-	0x0a, 0x8c, 0x3e, 0x40, 0x57, 0x41, 0x17, 0xf0, 0xb7, 0x82, 0xf8, 0x5e, 0x30, 0xf5, 0x5d, 0x7d,
-	0x88, 0x36, 0x40, 0x3f, 0x8c, 0x87, 0x38, 0x64, 0x53, 0xb3, 0xc8, 0xfc, 0xab, 0x50, 0xfb, 0x42,
-	0x03, 0x24, 0x74, 0x25, 0xd8, 0x13, 0x8c, 0x79, 0xfd, 0xc7, 0x32, 0xda, 0xa5, 0x27, 0xb3, 0x04,
-	0xed, 0x1b, 0xb0, 0x66, 0xce, 0x91, 0x33, 0x24, 0x34, 0xf2, 0x3b, 0x9d, 0x00, 0x3e, 0x71, 0xf5,
-	0x3e, 0x8e, 0x93, 0x24, 0xca, 0xa1, 0x37, 0xe6, 0x0a, 0x01, 0x67, 0x2d, 0x93, 0xa5, 0x76, 0x31,
-	0x4b, 0x01, 0x4e, 0xcb, 0xa3, 0x2a, 0x5c, 0x55, 0x8f, 0x7c, 0x9c, 0x53, 0xa8, 0x7d, 0xa6, 0xc1,
-	0xaa, 0x52, 0xfe, 0x49, 0x22, 0x0a, 0x42, 0x33, 0xc4, 0x34, 0x9e, 0x4b, 0x64, 0x3e, 0x41, 0x54,
-	0x6e, 0xac, 0xba, 0x86, 0x7e, 0x08, 0xdf, 0x9f, 0x63, 0x89, 0xe2, 0x64, 0xe3, 0x3e, 0xf6, 0xce,
-	0x28, 0x5e, 0xde, 0x84, 0xeb, 0x73, 0x62, 0xf7, 0x1c, 0x8f, 0x80, 0x29, 0x2b, 0xcf, 0x69, 0x4f,
-	0x7d, 0x9f, 0x18, 0xce, 0xd5, 0x86, 0xa0, 0xcf, 0x3e, 0x1f, 0x90, 0xf0, 0x36, 0x5c, 0x97, 0x1f,
-	0xb2, 0xf4, 0x25, 0x82, 0x12, 0x1b, 0x8f, 0x83, 0x33, 0x2c, 0x48, 0x1a, 0x45, 0x49, 0xec, 0x84,
-	0xb1, 0xa0, 0x64, 0x48, 0xf4, 0xba, 0x71, 0x30, 0x11, 0x84, 0x2c, 0xb1, 0xf2, 0xc0, 0x1b, 0x8d,
-	0x3e, 0x0e, 0xc6, 0x27, 0x1e, 0xd6, 0x73, 0xb5, 0x77, 0x95, 0x8b, 0x33, 0x61, 0x13, 0x1c, 0x30,
-	0x8a, 0xbe, 0x44, 0xaa, 0x90, 0xe9, 0x8b, 0xa1, 0xb6, 0xdf, 0x7a, 0xf6, 0xf5, 0xd6, 0xd2, 0x97,
-	0xcf, 0xb7, 0xb4, 0x67, 0xcf, 0xb7, 0xb4, 0x7f, 0x3e, 0xdf, 0xd2, 0x3e, 0xbe, 0x2d, 0xfd, 0xea,
-	0x1d, 0x3b, 0x71, 0xe8, 0x3d, 0x09, 0x42, 0x6f, 0xe0, 0xf9, 0x62, 0xe0, 0xe3, 0x9d, 0xc9, 0xe3,
-	0xc1, 0xce, 0xe4, 0x64, 0x27, 0x2d, 0xc2, 0x27, 0x79, 0xfa, 0x9f, 0xf7, 0xf6, 0x7f, 0x02, 0x00,
-	0x00, 0xff, 0xff, 0x70, 0xf8, 0xb0, 0xd3, 0x46, 0x1e, 0x00, 0x00,
+	// 2515 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x39, 0x4f, 0x6f, 0x1b, 0xc7,
+	0xf5, 0x5a, 0x92, 0xa2, 0xc8, 0x47, 0x49, 0x5e, 0x8d, 0x24, 0x9b, 0x51, 0xf2, 0x93, 0xf5, 0xdb,
+	0xa6, 0x86, 0xcb, 0x24, 0x14, 0x20, 0xc3, 0x45, 0xd3, 0x2a, 0x36, 0x28, 0x2e, 0x6d, 0x31, 0xa6,
+	0x57, 0xea, 0x92, 0xea, 0x21, 0x40, 0xa0, 0xae, 0xb8, 0x23, 0x72, 0x6b, 0x72, 0x97, 0xdd, 0x5d,
+	0xaa, 0x56, 0x4e, 0xed, 0xc1, 0x05, 0x8a, 0x9e, 0x7a, 0x0b, 0x8a, 0xa2, 0x40, 0x3f, 0x41, 0x0f,
+	0xcd, 0x27, 0x28, 0x72, 0xc8, 0xd1, 0x97, 0x5e, 0x83, 0xd6, 0x97, 0xf6, 0xd6, 0xaf, 0x50, 0xcc,
+	0xbf, 0xdd, 0x19, 0x92, 0x92, 0xec, 0xd8, 0x45, 0xdb, 0xdb, 0xce, 0xfb, 0x37, 0x6f, 0xde, 0xbc,
+	0x7f, 0xf3, 0x16, 0xf4, 0x41, 0xd0, 0x8b, 0x70, 0x78, 0xe6, 0x75, 0x71, 0x75, 0x14, 0x06, 0x71,
+	0x80, 0x20, 0x85, 0x6c, 0x7c, 0xd0, 0xf3, 0xe2, 0xfe, 0xf8, 0xa4, 0xda, 0x0d, 0x86, 0xdb, 0xbd,
+	0xa0, 0x17, 0x6c, 0x53, 0x92, 0x93, 0xf1, 0x29, 0x5d, 0xd1, 0x05, 0xfd, 0x62, 0xac, 0x1b, 0xcb,
+	0x43, 0x1c, 0x3b, 0xae, 0x13, 0x3b, 0x6c, 0x6d, 0x3c, 0xd3, 0x20, 0x5f, 0xb7, 0xac, 0xc0, 0xc5,
+	0x08, 0x41, 0xee, 0xe8, 0xa8, 0x69, 0x96, 0xb5, 0x2d, 0xed, 0x76, 0xd1, 0xa6, 0xdf, 0xe8, 0x16,
+	0x2c, 0xb7, 0xd9, 0x46, 0x35, 0xd7, 0x0d, 0x71, 0x14, 0x95, 0x33, 0x14, 0x3b, 0x01, 0x25, 0xbc,
+	0x1d, 0xaf, 0xfb, 0xa4, 0x9c, 0xdd, 0xd2, 0x6e, 0xe7, 0x6c, 0xfa, 0x8d, 0xde, 0x83, 0xf9, 0x76,
+	0xec, 0xc4, 0xb8, 0x9c, 0xdb, 0xd2, 0x6e, 0x2f, 0xef, 0xac, 0x57, 0xa5, 0x73, 0x90, 0x0d, 0x29,
+	0xd2, 0x66, 0x34, 0x54, 0x0f, 0xf3, 0xbf, 0x40, 0x8f, 0x2f, 0x35, 0x58, 0x68, 0x05, 0xbd, 0xff,
+	0xb8, 0x22, 0x68, 0x17, 0x0a, 0x36, 0x1e, 0x0d, 0xbc, 0xae, 0x13, 0x95, 0xe7, 0xb7, 0xb2, 0xb7,
+	0x4b, 0x3b, 0x1b, 0x32, 0x7d, 0x2b, 0xe8, 0x71, 0x74, 0xd3, 0x3f, 0x0d, 0xf6, 0x72, 0x5f, 0x7d,
+	0x7d, 0x73, 0xce, 0x4e, 0x38, 0x8c, 0x7f, 0x6a, 0xb0, 0xd8, 0x0a, 0x7a, 0xed, 0xbe, 0x13, 0xba,
+	0x84, 0x00, 0x95, 0x61, 0x81, 0x2d, 0xd8, 0x71, 0x72, 0xb6, 0x58, 0xa2, 0x3d, 0x69, 0xa3, 0x0c,
+	0xdd, 0xe8, 0xd6, 0xc4, 0x46, 0x89, 0x94, 0xaa, 0x20, 0x6c, 0xf8, 0x71, 0x78, 0x9e, 0x6e, 0x87,
+	0xd6, 0x60, 0xbe, 0x31, 0x0a, 0xba, 0x7d, 0x7e, 0x5c, 0xb6, 0x40, 0x1b, 0x50, 0x68, 0x61, 0xc7,
+	0xc5, 0x61, 0xd3, 0xa4, 0x47, 0xce, 0xd9, 0xc9, 0x9a, 0xda, 0x07, 0x87, 0xc3, 0xf2, 0x3c, 0xb7,
+	0x0f, 0x0e, 0x87, 0x1b, 0x3f, 0x80, 0x25, 0x65, 0x03, 0xa4, 0x43, 0xf6, 0x09, 0x3e, 0xe7, 0x0a,
+	0x93, 0x4f, 0xb2, 0xd1, 0x99, 0x33, 0x18, 0x63, 0x6e, 0x75, 0xb6, 0xf8, 0x7e, 0xe6, 0x7b, 0x9a,
+	0x71, 0x06, 0xcb, 0xaa, 0x4d, 0xd0, 0x03, 0xd5, 0x04, 0x54, 0x4c, 0x69, 0xa7, 0x7c, 0xd1, 0xe1,
+	0xf6, 0x0a, 0xc4, 0x86, 0xcf, 0xbf, 0xbe, 0xa9, 0xd9, 0xaa, 0xe9, 0xde, 0x81, 0xa2, 0x10, 0x6b,
+	0xd2, 0x7d, 0x73, 0x76, 0x0a, 0x30, 0x2c, 0xd0, 0xeb, 0x56, 0x3b, 0x0e, 0x42, 0xbc, 0x8f, 0x9d,
+	0x30, 0x3e, 0xc1, 0x4e, 0xfc, 0x3a, 0x8e, 0x63, 0xfc, 0x45, 0x83, 0x15, 0xb2, 0xfd, 0xd5, 0x12,
+	0xb7, 0xa0, 0x64, 0x3b, 0xa7, 0xb1, 0x2a, 0x4e, 0x06, 0xcd, 0xd8, 0x33, 0x3b, 0xd3, 0x59, 0xdf,
+	0x85, 0xa5, 0x87, 0x41, 0x14, 0x79, 0x23, 0x41, 0x96, 0xa3, 0x64, 0x2a, 0xf0, 0x35, 0x3d, 0xb2,
+	0x01, 0x25, 0xd3, 0x7a, 0x19, 0x7f, 0xbc, 0xdc, 0xdc, 0xcf, 0x34, 0xd0, 0xcd, 0x37, 0x68, 0x6f,
+	0x74, 0x17, 0xf2, 0x74, 0x67, 0x62, 0x1b, 0x72, 0xa6, 0x1b, 0xf2, 0x99, 0x24, 0x8d, 0xf9, 0x81,
+	0x38, 0xb1, 0xf1, 0xab, 0x0c, 0x14, 0xec, 0xf6, 0x63, 0x16, 0xab, 0x3a, 0x64, 0x3b, 0x51, 0x20,
+	0xfc, 0xb4, 0x13, 0x05, 0xc4, 0x4f, 0x9b, 0xbe, 0x8b, 0x9f, 0xf2, 0x03, 0xb0, 0x05, 0xb1, 0x73,
+	0x0b, 0x3b, 0x11, 0xde, 0x0f, 0x06, 0x2c, 0x2a, 0x58, 0xb8, 0xa8, 0x40, 0x64, 0xc0, 0x62, 0x27,
+	0x1c, 0xfb, 0x5d, 0x27, 0xc6, 0x6e, 0x2b, 0xf2, 0x79, 0xe8, 0x28, 0x30, 0xf4, 0x31, 0x2c, 0x32,
+	0x26, 0x2f, 0x8a, 0x83, 0xf0, 0x9c, 0xdf, 0x87, 0x12, 0xb8, 0x42, 0xbb, 0xaa, 0x4c, 0xc8, 0x02,
+	0x57, 0xe1, 0xdd, 0xb8, 0x0f, 0x2b, 0x53, 0x24, 0x57, 0x85, 0x5e, 0x4e, 0x0e, 0xbd, 0x4f, 0xa1,
+	0x48, 0x2f, 0xbf, 0x1b, 0x84, 0x2e, 0x61, 0x24, 0x4a, 0x73, 0x46, 0xa2, 0x6b, 0x05, 0x72, 0x9d,
+	0xf3, 0x11, 0xe3, 0x5b, 0xde, 0xb9, 0xae, 0xe8, 0x48, 0x79, 0x08, 0xd6, 0xa6, 0x34, 0xe4, 0x26,
+	0x4d, 0x27, 0x76, 0xa8, 0x61, 0x16, 0x6d, 0xfa, 0x6d, 0x7c, 0xae, 0x01, 0x50, 0xf9, 0x3f, 0x1d,
+	0xe3, 0x88, 0x5e, 0xb6, 0xe5, 0x0c, 0xb1, 0xb8, 0x6c, 0xf2, 0x2d, 0x7b, 0x53, 0x46, 0xf5, 0x26,
+	0xae, 0x4e, 0x36, 0x55, 0xa7, 0x0c, 0x0b, 0x8f, 0x9d, 0xa7, 0x6d, 0xef, 0x33, 0xcc, 0x2d, 0x2b,
+	0x96, 0xc4, 0xf3, 0xc4, 0x85, 0x9b, 0x3c, 0x31, 0xa5, 0x00, 0xaa, 0x9a, 0xd5, 0x34, 0xcb, 0x79,
+	0x96, 0xb1, 0xc8, 0xb7, 0x61, 0x00, 0x74, 0xa2, 0x40, 0x68, 0xb6, 0x06, 0xf3, 0xf5, 0x60, 0xec,
+	0xc7, 0xfc, 0xf0, 0x6c, 0x61, 0xfc, 0x39, 0x0b, 0x0b, 0x82, 0x82, 0xfa, 0x36, 0xfd, 0x4c, 0xfc,
+	0x3e, 0x05, 0xa0, 0x2a, 0xe4, 0x1f, 0xe3, 0xb8, 0x1f, 0xb8, 0xb3, 0x4c, 0xc5, 0x30, 0xd4, 0x54,
+	0x9c, 0x8a, 0x9c, 0xa4, 0xe3, 0x0d, 0x71, 0x30, 0x8e, 0xe9, 0xf9, 0xb2, 0xb6, 0x58, 0xa2, 0x5d,
+	0xd9, 0x62, 0xf4, 0x98, 0x25, 0x55, 0x5a, 0x8a, 0xe5, 0x7e, 0x2d, 0x5b, 0xf8, 0x21, 0x4d, 0x9c,
+	0x49, 0x78, 0x51, 0x53, 0x94, 0x76, 0xfe, 0x6f, 0x32, 0x71, 0x2a, 0x31, 0xc8, 0xc5, 0x28, 0x8c,
+	0xc8, 0x84, 0x52, 0xdd, 0x4a, 0xe5, 0xe4, 0xa9, 0x9c, 0x77, 0x64, 0x39, 0x93, 0xa9, 0x93, 0x8b,
+	0x91, 0xd9, 0x88, 0x14, 0x53, 0x92, 0xb2, 0x30, 0x2d, 0xc5, 0xbc, 0x40, 0x8a, 0xc4, 0x46, 0x4c,
+	0x92, 0x5e, 0x55, 0xb9, 0x30, 0x6d, 0x92, 0x14, 0x2b, 0x4c, 0x92, 0x42, 0x8c, 0x36, 0x94, 0xa8,
+	0x81, 0xa2, 0x51, 0xe0, 0x47, 0xf8, 0x92, 0xec, 0xc5, 0xfd, 0x2d, 0xa3, 0xf8, 0x5b, 0xcb, 0x89,
+	0xe2, 0xd4, 0x0b, 0xc5, 0xd2, 0xf8, 0x43, 0x8e, 0x64, 0x54, 0x2e, 0xf2, 0xcd, 0xba, 0xc6, 0x1d,
+	0x28, 0x36, 0xc2, 0x30, 0x08, 0xeb, 0x81, 0x8b, 0xe9, 0xb6, 0x13, 0xed, 0x46, 0x82, 0xb4, 0x53,
+	0x3a, 0x92, 0x78, 0xe8, 0xe2, 0x31, 0x8e, 0x22, 0xa7, 0x87, 0x79, 0x15, 0x50, 0x60, 0x68, 0x13,
+	0xa0, 0x19, 0xed, 0xd7, 0x1e, 0x61, 0x3c, 0xc2, 0x21, 0xf5, 0x8c, 0x82, 0x2d, 0x41, 0xd0, 0x7d,
+	0xc5, 0x50, 0xfc, 0xca, 0x6f, 0x4c, 0xb9, 0x1e, 0x43, 0x8b, 0x7b, 0x92, 0x4d, 0xbb, 0x07, 0x8b,
+	0xf5, 0x60, 0x38, 0x74, 0x7c, 0x77, 0xcf, 0x89, 0xbb, 0x7d, 0x7e, 0xdd, 0x4a, 0xd5, 0x96, 0xf1,
+	0xc2, 0xef, 0x64, 0x18, 0x51, 0x82, 0xde, 0x1d, 0x57, 0xa2, 0x30, 0xad, 0x84, 0x84, 0x16, 0x4a,
+	0x48, 0x20, 0xb4, 0x0f, 0xcb, 0xf5, 0xc1, 0x38, 0x8a, 0x71, 0x68, 0xe2, 0xd8, 0xf1, 0x06, 0x51,
+	0xb9, 0x48, 0x65, 0x28, 0x05, 0x4f, 0xa5, 0xe0, 0x62, 0x26, 0xf8, 0xd0, 0x3d, 0x28, 0xa6, 0x1d,
+	0x08, 0x50, 0x21, 0x5b, 0xb2, 0x90, 0x04, 0xf9, 0xc3, 0x31, 0x0e, 0xcf, 0x6d, 0x1c, 0x8d, 0x07,
+	0xb1, 0x9d, 0xb2, 0x18, 0x1f, 0xd3, 0x6e, 0x80, 0xe5, 0xc9, 0x44, 0xbd, 0xbb, 0x24, 0xa3, 0x10,
+	0x48, 0x54, 0xd6, 0x68, 0xe2, 0x5f, 0x9f, 0x32, 0x30, 0xc1, 0x72, 0x95, 0x04, 0xad, 0xf1, 0x2d,
+	0xc5, 0x2c, 0x24, 0x5d, 0xfd, 0x88, 0x26, 0x74, 0x9e, 0xae, 0xe8, 0xc2, 0xf8, 0xb5, 0x46, 0x84,
+	0xd3, 0x72, 0x3b, 0xb3, 0xae, 0x5e, 0x9c, 0x6a, 0x95, 0xc2, 0x9d, 0x9d, 0x28, 0xdc, 0x69, 0x8b,
+	0x98, 0x93, 0x5b, 0xc4, 0x4d, 0x9a, 0xa8, 0xd4, 0x9c, 0x2b, 0x41, 0x8c, 0xdf, 0x66, 0x88, 0x3b,
+	0xf8, 0xa7, 0x5e, 0xaf, 0xde, 0x77, 0xfc, 0x1e, 0x46, 0x77, 0x12, 0xed, 0x78, 0x3f, 0xb7, 0xaa,
+	0xd6, 0x13, 0x8a, 0x4a, 0x0f, 0xce, 0xce, 0xb1, 0x0b, 0xc0, 0xd8, 0xa5, 0x3a, 0xa4, 0xa6, 0x21,
+	0x69, 0x0b, 0x1a, 0x47, 0x12, 0x3d, 0xea, 0xc0, 0x72, 0xd3, 0xf7, 0x62, 0xcf, 0x19, 0x3c, 0xc6,
+	0xc3, 0x13, 0x1c, 0x8a, 0x4e, 0xe1, 0xfd, 0x8b, 0x24, 0x54, 0x55, 0x72, 0x56, 0x73, 0x27, 0x64,
+	0x6c, 0xd4, 0x60, 0x75, 0x06, 0xd9, 0x2b, 0xb5, 0xbc, 0xdf, 0x81, 0xa5, 0x76, 0x7f, 0x1c, 0xbb,
+	0xc1, 0xcf, 0x7c, 0x9a, 0xff, 0xe8, 0xdd, 0x90, 0x8f, 0xe4, 0xca, 0xc4, 0xd2, 0x78, 0x96, 0x81,
+	0x6b, 0xed, 0x6e, 0x1f, 0xbb, 0xe3, 0x01, 0xe6, 0xa1, 0x32, 0xf3, 0x76, 0xdf, 0x85, 0xa5, 0xbd,
+	0x20, 0x88, 0xa3, 0x38, 0x74, 0x46, 0x23, 0xcf, 0xef, 0xd1, 0x4d, 0x0b, 0xb6, 0x0a, 0x44, 0xbb,
+	0xea, 0xa5, 0xd0, 0xcb, 0x9e, 0x8a, 0xd1, 0x14, 0x6f, 0xab, 0x57, 0xf8, 0x21, 0x94, 0x78, 0x0f,
+	0x46, 0xaf, 0x83, 0x3d, 0x86, 0x94, 0xe8, 0x94, 0xd0, 0xb6, 0x4c, 0x8b, 0xee, 0x4f, 0x9c, 0x98,
+	0x97, 0xa6, 0xb7, 0xd4, 0x88, 0x92, 0x08, 0x6c, 0x95, 0xde, 0x70, 0xd4, 0xec, 0x92, 0x3c, 0x43,
+	0xb4, 0xf4, 0x19, 0x82, 0x3e, 0x82, 0x02, 0xa7, 0x11, 0x0f, 0xa2, 0xb7, 0x15, 0xf9, 0xaa, 0x19,
+	0x45, 0xa3, 0x2b, 0x58, 0x8c, 0x26, 0x29, 0x7a, 0xcc, 0xee, 0xa4, 0xd1, 0x15, 0x0f, 0x41, 0x4d,
+	0x7a, 0x08, 0xbe, 0xec, 0x5b, 0xe0, 0x77, 0x1a, 0x2c, 0x10, 0x59, 0xa4, 0xc7, 0xfc, 0x08, 0xf2,
+	0x54, 0xa8, 0x08, 0xf9, 0x9b, 0x93, 0x65, 0x94, 0xb4, 0x7a, 0x8c, 0x82, 0x7a, 0x52, 0xd2, 0xaf,
+	0x52, 0xd0, 0x86, 0x0d, 0x25, 0x09, 0x29, 0xbb, 0x59, 0x91, 0xb9, 0xd9, 0x07, 0xb2, 0x9b, 0x4d,
+	0x64, 0x4b, 0xe9, 0x3c, 0xb2, 0xff, 0xfd, 0x5c, 0xa3, 0x3d, 0xfd, 0x9b, 0x38, 0xea, 0x37, 0x6d,
+	0xc3, 0x89, 0x85, 0xcc, 0x97, 0xb1, 0x90, 0xf9, 0xef, 0xb5, 0x90, 0x39, 0xdb, 0x42, 0x7f, 0xd4,
+	0x26, 0x0b, 0x09, 0xda, 0x21, 0x0a, 0x93, 0xd7, 0xbe, 0x50, 0x13, 0xa9, 0x72, 0x08, 0x4a, 0xe4,
+	0x2f, 0x4e, 0x48, 0x78, 0xea, 0x9c, 0x27, 0x33, 0xcd, 0x53, 0x57, 0x78, 0x38, 0x21, 0xba, 0x0b,
+	0x05, 0x3e, 0xc7, 0x10, 0x26, 0x5d, 0x9d, 0x28, 0x12, 0x12, 0x57, 0x42, 0x6a, 0xfc, 0x52, 0x83,
+	0x12, 0xd7, 0x98, 0xde, 0xe9, 0x87, 0x50, 0xe0, 0xd6, 0x17, 0xfa, 0xde, 0xa8, 0x26, 0x23, 0x24,
+	0x8e, 0x51, 0xaa, 0x4d, 0x42, 0x8e, 0x76, 0xe9, 0xb3, 0x80, 0xf3, 0x32, 0xbd, 0xcb, 0x29, 0xaf,
+	0x40, 0x29, 0xcc, 0x29, 0x83, 0xf1, 0x1b, 0x0d, 0xd6, 0x79, 0x82, 0xe4, 0xfa, 0x88, 0xf6, 0xf4,
+	0x16, 0x2c, 0x5b, 0xe3, 0xe1, 0xc1, 0x69, 0x2a, 0x9c, 0x39, 0xdc, 0x04, 0x94, 0xe4, 0x32, 0x0a,
+	0x49, 0xf4, 0x67, 0xf5, 0x4a, 0x05, 0xa2, 0x0a, 0xe8, 0x82, 0x2f, 0x79, 0xdd, 0xb2, 0xe2, 0x35,
+	0x05, 0x37, 0x9e, 0xf3, 0xa9, 0xca, 0xa5, 0x1e, 0xff, 0xbf, 0xf5, 0x2c, 0xff, 0x22, 0x43, 0xfd,
+	0x84, 0x45, 0xd0, 0xbd, 0x24, 0x08, 0xd9, 0x55, 0x6f, 0x4d, 0xb5, 0xfc, 0x34, 0x84, 0x28, 0x89,
+	0x1a, 0x42, 0xcc, 0x96, 0xf7, 0x92, 0x08, 0xcc, 0x5c, 0xc6, 0x7f, 0x61, 0x08, 0xb6, 0xa1, 0x24,
+	0x09, 0x9f, 0x51, 0x0b, 0xab, 0x6a, 0x08, 0x5e, 0x38, 0xcb, 0x91, 0x62, 0x90, 0x0a, 0xbd, 0x34,
+	0xae, 0xaf, 0x12, 0x3a, 0x2b, 0xb0, 0x7f, 0x41, 0xfa, 0x92, 0x3e, 0xee, 0x3e, 0xc1, 0x21, 0x33,
+	0xdd, 0x2c, 0x4f, 0xb8, 0xaf, 0x84, 0xd2, 0xcc, 0xc4, 0x9a, 0xa2, 0x93, 0x97, 0x8f, 0x14, 0x7c,
+	0x77, 0x92, 0xe4, 0xc6, 0x4b, 0xec, 0xea, 0x8c, 0x94, 0x96, 0x26, 0x0b, 0xa6, 0xc9, 0x77, 0xd3,
+	0x0b, 0xe5, 0x2f, 0xbf, 0xb5, 0x59, 0xd7, 0x20, 0x45, 0x3e, 0xe3, 0xdb, 0x16, 0xd3, 0xc9, 0x79,
+	0x5a, 0x90, 0x95, 0x9a, 0x2a, 0xda, 0x7b, 0x65, 0x54, 0xfa, 0xc5, 0x3c, 0xe8, 0x02, 0x91, 0x8c,
+	0x42, 0x66, 0xd9, 0xe1, 0x3a, 0xe4, 0x2d, 0xfc, 0x34, 0x4e, 0x3a, 0x46, 0xbe, 0x4a, 0x8a, 0x6f,
+	0x56, 0x2a, 0xbe, 0xdb, 0xea, 0x8c, 0xf4, 0x4a, 0x2d, 0x90, 0x0b, 0xfa, 0x44, 0x45, 0x16, 0x61,
+	0xb0, 0x33, 0x8b, 0x37, 0x99, 0x8a, 0x4c, 0x32, 0xc9, 0xfe, 0x38, 0x25, 0x11, 0x35, 0xe5, 0x5c,
+	0x96, 0xa7, 0xe2, 0xdf, 0xbb, 0x54, 0x7c, 0x42, 0xcd, 0xba, 0xbf, 0x94, 0x9b, 0x5c, 0x2a, 0x2f,
+	0xd7, 0xfc, 0x6d, 0xb3, 0x3a, 0xa3, 0x92, 0xa7, 0xd9, 0x9c, 0x9d, 0x52, 0xf2, 0x84, 0xc2, 0x37,
+	0xf2, 0x84, 0xe2, 0x2b, 0x78, 0xc2, 0x84, 0xdf, 0xc2, 0xab, 0xfa, 0xed, 0xc6, 0xa7, 0xb0, 0x3e,
+	0xd3, 0xbc, 0xaf, 0x18, 0x7c, 0x72, 0xa7, 0x26, 0x47, 0xf4, 0x2e, 0x1d, 0xf5, 0x5e, 0x90, 0x29,
+	0x8a, 0x57, 0x4d, 0xab, 0x9a, 0x50, 0x92, 0xa7, 0xc4, 0xaf, 0x33, 0xab, 0xfd, 0x7d, 0x06, 0xd6,
+	0x66, 0x3d, 0xe0, 0x2e, 0x99, 0x0f, 0x1c, 0x4e, 0x4d, 0xdb, 0xab, 0x57, 0x3d, 0x07, 0xd5, 0xa9,
+	0xfb, 0x64, 0x06, 0x7f, 0x43, 0xb3, 0xf7, 0xce, 0xd5, 0xb3, 0xf7, 0xcb, 0xfa, 0x1f, 0xc9, 0xa2,
+	0x92, 0xad, 0x2b, 0x3f, 0x06, 0x38, 0x1a, 0xb9, 0x4e, 0xcc, 0xba, 0xf7, 0x1b, 0xb0, 0xaa, 0x4c,
+	0x3a, 0x19, 0x4a, 0x9f, 0x43, 0xeb, 0xb0, 0x22, 0xa6, 0x9b, 0xad, 0xb6, 0xc5, 0xc1, 0x1a, 0x5a,
+	0x85, 0x6b, 0x47, 0x11, 0x0e, 0xa9, 0x3e, 0x1c, 0x98, 0x41, 0x4b, 0x50, 0xec, 0xb4, 0x0f, 0xf8,
+	0x32, 0x5b, 0xa9, 0x42, 0x31, 0xf9, 0x75, 0x82, 0xae, 0x41, 0xc9, 0x0a, 0xc2, 0xa1, 0x33, 0xa0,
+	0x4b, 0x7d, 0x0e, 0xe9, 0xb0, 0xc8, 0x47, 0x62, 0x0c, 0xa2, 0x55, 0xfe, 0xae, 0x01, 0xa4, 0xf3,
+	0x12, 0xb4, 0x0c, 0xd0, 0x69, 0x1f, 0x1c, 0x1f, 0x1d, 0x9a, 0xb5, 0x4e, 0x43, 0x9f, 0x43, 0x00,
+	0xf9, 0xda, 0xe1, 0x61, 0xc3, 0x32, 0x75, 0x0d, 0x15, 0x20, 0x67, 0x37, 0x6a, 0xa6, 0x9e, 0x41,
+	0x8b, 0x50, 0xe8, 0xd8, 0x47, 0x56, 0x9d, 0xd0, 0x64, 0x89, 0xd0, 0x87, 0x8d, 0xce, 0x71, 0x02,
+	0xc9, 0xa1, 0x12, 0x2c, 0xd4, 0x0f, 0x2c, 0xab, 0x51, 0xef, 0xe8, 0xf3, 0x44, 0x24, 0x5f, 0x1c,
+	0xdb, 0x07, 0x7a, 0x1e, 0xad, 0xc0, 0x52, 0xeb, 0xe0, 0xe1, 0xf1, 0x7e, 0xa3, 0x66, 0x77, 0xf6,
+	0x1a, 0xb5, 0x8e, 0xbe, 0x40, 0x24, 0xd4, 0x2d, 0x09, 0x52, 0x20, 0x10, 0x53, 0x86, 0x14, 0x11,
+	0x82, 0xe5, 0xfa, 0x7e, 0xa3, 0xfe, 0xe8, 0x78, 0xbf, 0xf6, 0xa8, 0xd1, 0x38, 0x6c, 0xd8, 0x3a,
+	0x10, 0x03, 0x92, 0x9d, 0xeb, 0xad, 0xa3, 0x76, 0xa7, 0x61, 0x1f, 0x9b, 0x8d, 0x4e, 0xad, 0xd9,
+	0x6a, 0xeb, 0x25, 0x42, 0x4c, 0x10, 0xed, 0xfd, 0x9a, 0x6d, 0x1e, 0x37, 0xad, 0x07, 0x07, 0xfa,
+	0x62, 0xc5, 0x02, 0x48, 0xc7, 0xab, 0x44, 0x2b, 0x62, 0x4b, 0x06, 0xd1, 0xe7, 0xc8, 0x91, 0x9a,
+	0x7e, 0x8c, 0x43, 0xdf, 0x19, 0xe8, 0x1a, 0x31, 0x1c, 0xbd, 0x99, 0xc4, 0xca, 0x2b, 0x7c, 0x52,
+	0x6d, 0xe3, 0x9f, 0xe0, 0x6e, 0x8c, 0x5d, 0x3d, 0x5b, 0xf9, 0x53, 0x46, 0x9a, 0x29, 0x91, 0x23,
+	0x5b, 0x01, 0x5d, 0xea, 0x73, 0x64, 0xc1, 0xcd, 0xac, 0x6b, 0x44, 0x72, 0xdd, 0xf1, 0xbb, 0x78,
+	0x80, 0x5d, 0x3d, 0x43, 0x0e, 0xd6, 0xf4, 0xcf, 0x9c, 0x81, 0xe7, 0x52, 0xd7, 0xd6, 0xb3, 0x44,
+	0x57, 0x0e, 0x11, 0x3c, 0x39, 0x09, 0x76, 0xe8, 0x9c, 0x0f, 0x02, 0xc7, 0xd5, 0xe7, 0xd1, 0x75,
+	0x40, 0x2a, 0xac, 0xed, 0x7d, 0x86, 0xf5, 0x3c, 0x91, 0x9f, 0x68, 0xb5, 0x40, 0x14, 0xa5, 0x82,
+	0xad, 0x20, 0xb6, 0xb1, 0xe3, 0x9e, 0x33, 0x5b, 0xb6, 0xcf, 0xa3, 0x18, 0x0f, 0xeb, 0x83, 0x20,
+	0xc2, 0xae, 0x5e, 0xa4, 0x8e, 0x17, 0xf9, 0xb5, 0x41, 0x48, 0x28, 0x92, 0x39, 0xba, 0xee, 0x12,
+	0xab, 0x1c, 0x8c, 0xe3, 0x83, 0x53, 0x9b, 0x3c, 0x4c, 0x75, 0x52, 0xbd, 0x96, 0xad, 0x20, 0x96,
+	0x9c, 0x54, 0x3f, 0x65, 0x4e, 0x15, 0x8b, 0x04, 0xaf, 0xf7, 0xd0, 0x1a, 0xe8, 0x22, 0xfd, 0x58,
+	0x41, 0xfc, 0x20, 0x18, 0xfb, 0xae, 0xde, 0x47, 0xeb, 0xa0, 0x1f, 0xc4, 0x7d, 0x1c, 0xb2, 0xad,
+	0x99, 0x65, 0xfe, 0xb1, 0x50, 0xf9, 0x52, 0x03, 0x24, 0x78, 0xa5, 0x50, 0x20, 0x7e, 0xe7, 0x75,
+	0x9f, 0xc8, 0x11, 0x20, 0x8d, 0x3c, 0x93, 0x08, 0x58, 0x87, 0x15, 0x73, 0x0a, 0x9c, 0x21, 0xa6,
+	0x91, 0xe7, 0xac, 0x22, 0x18, 0x88, 0xaa, 0x0f, 0x71, 0x9c, 0x04, 0x56, 0x0e, 0xbd, 0x35, 0x95,
+	0x88, 0x39, 0x6a, 0x9e, 0x1c, 0xb5, 0x8d, 0x59, 0x58, 0x70, 0x58, 0x1e, 0x95, 0x61, 0x4d, 0x6d,
+	0xb9, 0x39, 0x66, 0xa1, 0xf2, 0xb9, 0x06, 0x4b, 0x4a, 0xf9, 0x25, 0xc1, 0x29, 0x00, 0xf5, 0x10,
+	0x53, 0x7b, 0xce, 0x91, 0xfd, 0x04, 0x50, 0x99, 0x18, 0xe8, 0x1a, 0xfa, 0x36, 0xfc, 0xff, 0x14,
+	0x4a, 0x14, 0x07, 0x1b, 0x77, 0xb1, 0x77, 0x46, 0xfd, 0xe5, 0x6d, 0xb8, 0x31, 0x45, 0xf6, 0xc0,
+	0xf1, 0x88, 0x33, 0x65, 0xe5, 0x3d, 0xed, 0xb1, 0xef, 0x13, 0xc1, 0xb9, 0x4a, 0x1f, 0xf4, 0xc9,
+	0xf1, 0x0d, 0x31, 0x6f, 0xcd, 0x75, 0x79, 0x52, 0xd2, 0xe7, 0x88, 0x97, 0xd8, 0x78, 0x18, 0x9c,
+	0x61, 0x01, 0xd2, 0xa8, 0x97, 0xc4, 0x4e, 0x18, 0x0b, 0x48, 0x86, 0x58, 0xaf, 0x1d, 0x07, 0x23,
+	0x01, 0xc8, 0x12, 0x29, 0x8f, 0xbc, 0xc1, 0xe0, 0x93, 0x60, 0x78, 0xe2, 0x61, 0x3d, 0x57, 0x79,
+	0x5f, 0x19, 0x5c, 0x10, 0x34, 0xf1, 0x03, 0x06, 0xd1, 0xe7, 0x48, 0x66, 0x32, 0x7d, 0xb1, 0xd4,
+	0xf6, 0x1a, 0xcf, 0xff, 0xb6, 0x39, 0xf7, 0xd5, 0x8b, 0x4d, 0xed, 0xf9, 0x8b, 0x4d, 0xed, 0xaf,
+	0x2f, 0x36, 0xb5, 0x4f, 0xee, 0x48, 0xbf, 0xea, 0x87, 0x4e, 0x1c, 0x7a, 0x4f, 0x83, 0xd0, 0xeb,
+	0x79, 0xbe, 0x58, 0xf8, 0x78, 0x7b, 0xf4, 0xa4, 0xb7, 0x3d, 0x3a, 0xd9, 0x4e, 0x33, 0xeb, 0x49,
+	0x9e, 0xfe, 0xa7, 0xbf, 0xf3, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe9, 0x67, 0xba, 0xd0, 0x06,
+	0x20, 0x00, 0x00,
 }
 
 func (m *CNNode) Marshal() (dAtA []byte, err error) {
@@ -3767,6 +3923,18 @@ func (m *Response) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.ShardInfo != nil {
+		{
+			size, err := m.ShardInfo.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintLogservice(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x52
+	}
 	{
 		size, err := m.ClusterDetails.MarshalToSizedBuffer(dAtA[:i])
 		if err != nil {
@@ -4876,6 +5044,116 @@ func (m *HAKeeperRSMState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *ReplicaInfo) 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 *ReplicaInfo) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ReplicaInfo) 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.ServiceAddress) > 0 {
+		i -= len(m.ServiceAddress)
+		copy(dAtA[i:], m.ServiceAddress)
+		i = encodeVarintLogservice(dAtA, i, uint64(len(m.ServiceAddress)))
+		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 *ShardInfoQueryResult) 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 *ShardInfoQueryResult) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ShardInfoQueryResult) 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.Term != 0 {
+		i = encodeVarintLogservice(dAtA, i, uint64(m.Term))
+		i--
+		dAtA[i] = 0x28
+	}
+	if m.LeaderID != 0 {
+		i = encodeVarintLogservice(dAtA, i, uint64(m.LeaderID))
+		i--
+		dAtA[i] = 0x20
+	}
+	if m.Epoch != 0 {
+		i = encodeVarintLogservice(dAtA, i, uint64(m.Epoch))
+		i--
+		dAtA[i] = 0x18
+	}
+	if len(m.Replicas) > 0 {
+		for k := range m.Replicas {
+			v := m.Replicas[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 = encodeVarintLogservice(dAtA, i, uint64(k))
+			i--
+			dAtA[i] = 0x8
+			i = encodeVarintLogservice(dAtA, i, uint64(baseI-i))
+			i--
+			dAtA[i] = 0x12
+		}
+	}
+	if m.ShardID != 0 {
+		i = encodeVarintLogservice(dAtA, i, uint64(m.ShardID))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintLogservice(dAtA []byte, offset int, v uint64) int {
 	offset -= sovLogservice(v)
 	base := offset
@@ -5300,6 +5578,10 @@ func (m *Response) Size() (n int) {
 	n += 1 + l + sovLogservice(uint64(l))
 	l = m.ClusterDetails.Size()
 	n += 1 + l + sovLogservice(uint64(l))
+	if m.ShardInfo != nil {
+		l = m.ShardInfo.Size()
+		n += 1 + l + sovLogservice(uint64(l))
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -5756,31 +6038,84 @@ func (m *HAKeeperRSMState) Size() (n int) {
 	return n
 }
 
-func sovLogservice(x uint64) (n int) {
-	return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozLogservice(x uint64) (n int) {
-	return sovLogservice(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+func (m *ReplicaInfo) 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.ServiceAddress)
+	if l > 0 {
+		n += 1 + l + sovLogservice(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
 }
-func (m *CNNode) 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
-			}
+
+func (m *ShardInfoQueryResult) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.ShardID != 0 {
+		n += 1 + sovLogservice(uint64(m.ShardID))
+	}
+	if len(m.Replicas) > 0 {
+		for k, v := range m.Replicas {
+			_ = k
+			_ = v
+			l = v.Size()
+			mapEntrySize := 1 + sovLogservice(uint64(k)) + 1 + l + sovLogservice(uint64(l))
+			n += mapEntrySize + 1 + sovLogservice(uint64(mapEntrySize))
+		}
+	}
+	if m.Epoch != 0 {
+		n += 1 + sovLogservice(uint64(m.Epoch))
+	}
+	if m.LeaderID != 0 {
+		n += 1 + sovLogservice(uint64(m.LeaderID))
+	}
+	if m.Term != 0 {
+		n += 1 + sovLogservice(uint64(m.Term))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func sovLogservice(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozLogservice(x uint64) (n int) {
+	return sovLogservice(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *CNNode) 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)
@@ -8412,6 +8747,42 @@ func (m *Response) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 10:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ShardInfo", 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.ShardInfo == nil {
+				m.ShardInfo = &ShardInfoQueryResult{}
+			}
+			if err := m.ShardInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipLogservice(dAtA[iNdEx:])
@@ -11507,6 +11878,363 @@ func (m *HAKeeperRSMState) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *ReplicaInfo) 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: ReplicaInfo: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ReplicaInfo: 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 ServiceAddress", 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.ServiceAddress = 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 *ShardInfoQueryResult) 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: ShardInfoQueryResult: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ShardInfoQueryResult: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ShardID", wireType)
+			}
+			m.ShardID = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.ShardID |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Replicas", 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.Replicas == nil {
+				m.Replicas = make(map[uint64]ReplicaInfo)
+			}
+			var mapkey uint64
+			mapvalue := &ReplicaInfo{}
+			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 {
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowLogservice
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						mapkey |= uint64(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+				} 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 = &ReplicaInfo{}
+					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.Replicas[mapkey] = *mapvalue
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType)
+			}
+			m.Epoch = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Epoch |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LeaderID", wireType)
+			}
+			m.LeaderID = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.LeaderID |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
+			}
+			m.Term = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLogservice
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Term |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		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 skipLogservice(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go
index 5bfad02fc..bf6b8f140 100644
--- a/pkg/pb/plan/plan.pb.go
+++ b/pkg/pb/plan/plan.pb.go
@@ -913,6 +913,7 @@ func (m *Type) GetScale() int32 {
 type Const struct {
 	Isnull bool `protobuf:"varint,1,opt,name=isnull,proto3" json:"isnull,omitempty"`
 	// Types that are valid to be assigned to Value:
+	//
 	//	*Const_Ival
 	//	*Const_Dval
 	//	*Const_Sval
@@ -1727,6 +1728,7 @@ func (m *Function) GetArgs() []*Expr {
 type Expr struct {
 	Typ *Type `protobuf:"bytes,1,opt,name=typ,proto3" json:"typ,omitempty"`
 	// Types that are valid to be assigned to Expr:
+	//
 	//	*Expr_C
 	//	*Expr_P
 	//	*Expr_V
@@ -2448,6 +2450,7 @@ func (m *TableDef) GetDefs() []*TableDef_DefType {
 
 type TableDef_DefType struct {
 	// Types that are valid to be assigned to Def:
+	//
 	//	*TableDef_DefType_Pk
 	//	*TableDef_DefType_Idx
 	//	*TableDef_DefType_Properties
@@ -3504,9 +3507,10 @@ func (m *Query) GetHeadings() []string {
 }
 
 type TransationControl struct {
-	//TransationControl type
+	// TransationControl type
 	TclType TransationControl_TclType `protobuf:"varint,1,opt,name=tcl_type,json=tclType,proto3,enum=plan.TransationControl_TclType" json:"tcl_type,omitempty"`
 	// Types that are valid to be assigned to Action:
+	//
 	//	*TransationControl_Begin
 	//	*TransationControl_Commit
 	//	*TransationControl_Rollback
@@ -3756,6 +3760,7 @@ func (m *TransationRollback) GetCompletionType() TransationCompletionType {
 
 type Plan struct {
 	// Types that are valid to be assigned to Plan:
+	//
 	//	*Plan_Query
 	//	*Plan_Tcl
 	//	*Plan_Ddl
@@ -4016,9 +4021,10 @@ func (m *Column) GetColumn() []*Expr {
 }
 
 type DataControl struct {
-	//DataDefinition type
+	// DataDefinition type
 	DclType DataControl_DclType `protobuf:"varint,1,opt,name=dcl_type,json=dclType,proto3,enum=plan.DataControl_DclType" json:"dcl_type,omitempty"`
 	// Types that are valid to be assigned to Control:
+	//
 	//	*DataControl_SetVariables
 	//	*DataControl_Prepare
 	//	*DataControl_Execute
@@ -4139,13 +4145,14 @@ func (*DataControl) XXX_OneofWrappers() []interface{} {
 }
 
 type DataDefinition struct {
-	//DataDefinition type
+	// DataDefinition type
 	DdlType DataDefinition_DdlType `protobuf:"varint,1,opt,name=ddl_type,json=ddlType,proto3,enum=plan.DataDefinition_DdlType" json:"ddl_type,omitempty"`
-	//other show statement we will rewrite to a select statement
-	//then we will get a Query
-	//eg: 'show databases' will rewrite to 'select md.datname as `Database` from mo_database md'
+	// other show statement we will rewrite to a select statement
+	// then we will get a Query
+	// eg: 'show databases' will rewrite to 'select md.datname as `Database` from mo_database md'
 	Query *Query `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"`
 	// Types that are valid to be assigned to Definition:
+	//
 	//	*DataDefinition_CreateDatabase
 	//	*DataDefinition_AlterDatabase
 	//	*DataDefinition_DropDatabase
diff --git a/proto/logservice.proto b/proto/logservice.proto
index 8ddf4a275..805bd6877 100644
--- a/proto/logservice.proto
+++ b/proto/logservice.proto
@@ -150,6 +150,7 @@ enum MethodType {
   DN_HEARTBEAT   = 9;
   CHECK_HAKEEPER = 10;
   GET_CLUSTER_DETAILS = 11;
+  GET_SHARD_INFO = 12;
 };
 
 enum RecordType {
@@ -218,15 +219,16 @@ message LogResponse {
 }
 
 message Response {
-  uint64 RequestID          = 1;
-  MethodType Method         = 2;
-  ErrorCode ErrorCode       = 3;
-  string ErrorMessage       = 4;
-  bool IsHAKeeper           = 5;
-  LogResponse LogResponse   = 6 [(gogoproto.nullable) = false];
-  CommandBatch CommandBatch = 7 [(gogoproto.nullable) = false];
-  TsoResponse TsoResponse   = 8 [(gogoproto.nullable) = false];
-  ClusterDetails ClusterDetails = 9 [(gogoproto.nullable) = false];
+  uint64 RequestID               = 1;
+  MethodType Method              = 2;
+  ErrorCode ErrorCode            = 3;
+  string ErrorMessage            = 4;
+  bool IsHAKeeper                = 5;
+  LogResponse LogResponse        = 6 [(gogoproto.nullable) = false];
+  CommandBatch CommandBatch      = 7 [(gogoproto.nullable) = false];
+  TsoResponse TsoResponse        = 8 [(gogoproto.nullable) = false];
+  ClusterDetails ClusterDetails  = 9 [(gogoproto.nullable) = false];
+  ShardInfoQueryResult ShardInfo = 10;
 };
 
 message LogRecordResponse {
@@ -408,3 +410,18 @@ message HAKeeperRSMState {
   LogState LogState             = 9  [(gogoproto.nullable) = false];
   ClusterInfo ClusterInfo       = 10 [(gogoproto.nullable) = false];
 }
+
+// ReplicaInfo contains details of a replica
+message ReplicaInfo {
+  string UUID           = 1;
+  string ServiceAddress = 2;
+}
+
+// ShardInfoQueryResult contains the result of the shard info query. 
+message ShardInfoQueryResult {
+  uint64 ShardID                    = 1;
+  map<uint64, ReplicaInfo> Replicas = 2 [(gogoproto.nullable) = false];
+  uint64 Epoch                      = 3;
+  uint64 LeaderID                   = 4;
+  uint64 Term                       = 5;
+}


Test patch:
diff --git a/pkg/logservice/client_test.go b/pkg/logservice/client_test.go
index 5a4910e0c..ba921594e 100644
--- a/pkg/logservice/client_test.go
+++ b/pkg/logservice/client_test.go
@@ -115,6 +115,56 @@ func TestClientCanBeCreated(t *testing.T) {
 	runClientTest(t, true, fn)
 }
 
+func TestClientCanBeConnectedByReverseProxy(t *testing.T) {
+	defer leaktest.AfterTest(t)()
+	cfg := getServiceTestConfig()
+	defer vfs.ReportLeakedFD(cfg.FS, t)
+	service, err := NewService(cfg)
+	require.NoError(t, err)
+	defer func() {
+		assert.NoError(t, service.Close())
+	}()
+
+	init := make(map[uint64]string)
+	init[2] = service.ID()
+	assert.NoError(t, service.store.startReplica(1, 2, init, false))
+
+	scfg := ClientConfig{
+		LogShardID:       1,
+		DNReplicaID:      2,
+		ServiceAddresses: []string{"localhost:53032"}, // unreachable
+		DiscoveryAddress: testServiceAddress,
+	}
+
+	done := false
+	for i := 0; i < 1000; i++ {
+		si, ok, err := GetShardInfo(testServiceAddress, 1)
+		if err != nil {
+			time.Sleep(10 * time.Millisecond)
+			continue
+		}
+		done = true
+		require.NoError(t, err)
+		assert.True(t, ok)
+		assert.Equal(t, uint64(2), si.ReplicaID)
+		addr, ok := si.Replicas[si.ReplicaID]
+		assert.True(t, ok)
+		assert.Equal(t, testServiceAddress, addr)
+		break
+	}
+	if !done {
+		t.Fatalf("failed to get shard info")
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+	defer cancel()
+	c, err := NewClient(ctx, scfg)
+	require.NoError(t, err)
+	defer func() {
+		assert.NoError(t, c.Close())
+	}()
+}
+
 func TestClientGetTSOTimestamp(t *testing.T) {
 	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
 		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
diff --git a/pkg/logservice/hakeeper_client_test.go b/pkg/logservice/hakeeper_client_test.go
index fd0b626ef..a2f524c28 100644
--- a/pkg/logservice/hakeeper_client_test.go
+++ b/pkg/logservice/hakeeper_client_test.go
@@ -29,6 +29,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
+	"github.com/matrixorigin/matrixone/pkg/hakeeper"
 	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
 )
 
@@ -82,6 +83,61 @@ func TestHAKeeperClientCanNotConnectToNonHAKeeperNode(t *testing.T) {
 	runServiceTest(t, false, true, fn)
 }
 
+func TestHAKeeperClientConnectByReverseProxy(t *testing.T) {
+	fn := func(t *testing.T, s *Service) {
+		done := false
+		for i := 0; i < 1000; i++ {
+			si, ok, err := GetShardInfo(testServiceAddress, hakeeper.DefaultHAKeeperShardID)
+			if err != nil {
+				time.Sleep(10 * time.Millisecond)
+				continue
+			}
+			done = true
+			require.NoError(t, err)
+			assert.True(t, ok)
+			assert.Equal(t, uint64(1), si.ReplicaID)
+			addr, ok := si.Replicas[si.ReplicaID]
+			assert.True(t, ok)
+			assert.Equal(t, testServiceAddress, addr)
+			break
+		}
+		if !done {
+			t.Fatalf("failed to get shard info")
+		}
+		// now shard info can be queried
+		cfg := HAKeeperClientConfig{
+			ServiceAddresses: []string{"localhost:53033"}, // obvious not reachable
+			DiscoveryAddress: testServiceAddress,
+		}
+		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+		defer cancel()
+		c, err := NewLogHAKeeperClient(ctx, cfg)
+		require.NoError(t, err)
+		defer func() {
+			assert.NoError(t, c.Close())
+		}()
+
+		hb := s.store.getHeartbeatMessage()
+		cb, err := c.SendLogHeartbeat(ctx, hb)
+		require.NoError(t, err)
+		assert.Equal(t, 0, len(cb.Commands))
+
+		sc := pb.ScheduleCommand{
+			UUID:        s.ID(),
+			ServiceType: pb.DnService,
+			ShutdownStore: &pb.ShutdownStore{
+				StoreID: "hello world",
+			},
+		}
+		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
+		cb, err = c.SendLogHeartbeat(ctx, hb)
+		require.NoError(t, err)
+		require.Equal(t, 1, len(cb.Commands))
+		require.Equal(t, sc, cb.Commands[0])
+	}
+	runServiceTest(t, true, true, fn)
+}
+
 func TestHAKeeperClientSendCNHeartbeat(t *testing.T) {
 	fn := func(t *testing.T, s *Service) {
 		cfg := HAKeeperClientConfig{
diff --git a/pkg/logservice/service_test.go b/pkg/logservice/service_test.go
index 592e06b1c..ed40482e5 100644
--- a/pkg/logservice/service_test.go
+++ b/pkg/logservice/service_test.go
@@ -536,7 +536,7 @@ func TestShardInfoCanBeQueried(t *testing.T) {
 	// see whether gossip can finish syncing in 6 seconds time. also added some
 	// logging to get collect more details
 	for i := 0; i < 6000; i++ {
-		si1, ok := service1.GetShardInfo(1)
+		si1, ok := service1.getShardInfo(1)
 		if !ok || si1.LeaderID != 1 {
 			plog.Errorf("shard 1 info missing on service 1")
 			time.Sleep(time.Millisecond)
@@ -549,7 +549,7 @@ func TestShardInfoCanBeQueried(t *testing.T) {
 		assert.Equal(t, nhID1, ri.UUID)
 		assert.Equal(t, cfg1.ServiceAddress, ri.ServiceAddress)
 
-		si2, ok := service1.GetShardInfo(2)
+		si2, ok := service1.getShardInfo(2)
 		if !ok || si2.LeaderID != 1 {
 			plog.Errorf("shard 2 info missing on service 1")
 			time.Sleep(time.Millisecond)
@@ -562,7 +562,7 @@ func TestShardInfoCanBeQueried(t *testing.T) {
 		assert.Equal(t, nhID2, ri.UUID)
 		assert.Equal(t, cfg2.ServiceAddress, ri.ServiceAddress)
 
-		si1, ok = service2.GetShardInfo(1)
+		si1, ok = service2.getShardInfo(1)
 		if !ok || si1.LeaderID != 1 {
 			plog.Errorf("shard 1 info missing on service 2")
 			time.Sleep(time.Millisecond)
@@ -575,7 +575,7 @@ func TestShardInfoCanBeQueried(t *testing.T) {
 		assert.Equal(t, nhID1, ri.UUID)
 		assert.Equal(t, cfg1.ServiceAddress, ri.ServiceAddress)
 
-		si2, ok = service2.GetShardInfo(2)
+		si2, ok = service2.getShardInfo(2)
 		if !ok || si2.LeaderID != 1 {
 			plog.Errorf("shard 2 info missing on service 2")
 			time.Sleep(time.Millisecond)
@@ -664,7 +664,7 @@ func TestGossipConvergeDelay(t *testing.T) {
 		for i := 0; i < 48; i++ {
 			shardID := uint64(i/3 + 1)
 			service := services[i]
-			info, ok := service.GetShardInfo(shardID)
+			info, ok := service.getShardInfo(shardID)
 			if !ok || info.LeaderID == 0 {
 				done = false
 				wait()
@@ -688,7 +688,7 @@ func TestGossipConvergeDelay(t *testing.T) {
 		done := true
 		for i := 0; i < 48; i++ {
 			service := services[i]
-			info, ok := service.GetShardInfo(1)
+			info, ok := service.getShardInfo(1)
 			if !ok || info.LeaderID == 0 || len(info.Replicas) != 4 {
 				done = false
 				wait()
@@ -714,7 +714,7 @@ func TestGossipConvergeDelay(t *testing.T) {
 		done := true
 		for i := uint64(0); i < 16; i++ {
 			shardID := i + 1
-			info, ok := service.GetShardInfo(shardID)
+			info, ok := service.getShardInfo(shardID)
 			if !ok || info.LeaderID == 0 {
 				done = false
 				wait()
diff --git a/pkg/logservice/shardinfo_test.go b/pkg/logservice/shardinfo_test.go
new file mode 100644
index 000000000..90d7e2bac
--- /dev/null
+++ b/pkg/logservice/shardinfo_test.go
@@ -0,0 +1,53 @@
+// Copyright 2021 - 2022 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 logservice
+
+import (
+	"testing"
+	"time"
+
+	"github.com/lni/dragonboat/v4"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestGetShardInfo(t *testing.T) {
+	fn := func(t *testing.T, s *Service) {
+		done := false
+		for i := 0; i < 1000; i++ {
+			si, ok, err := GetShardInfo(testServiceAddress, 1)
+			if err != nil {
+				time.Sleep(10 * time.Millisecond)
+				continue
+			}
+			done = true
+			require.NoError(t, err)
+			assert.True(t, ok)
+			assert.Equal(t, uint64(1), si.ReplicaID)
+			addr, ok := si.Replicas[si.ReplicaID]
+			assert.True(t, ok)
+			assert.Equal(t, testServiceAddress, addr)
+			break
+		}
+		if !done {
+			t.Fatalf("failed to get shard info")
+		}
+		_, ok, err := GetShardInfo(testServiceAddress, 2)
+		require.Equal(t, dragonboat.ErrShardNotFound, err)
+		assert.False(t, ok)
+	}
+	runServiceTest(t, false, true, fn)
+}