Skip to content

Commit

Permalink
server: wrap etcd operations (#6025)
Browse files Browse the repository at this point in the history
ref #5836

Signed-off-by: Ryan Leung <rleungx@gmail.com>
  • Loading branch information
rleungx authored Feb 28, 2023
1 parent 9f13a58 commit f999ad5
Show file tree
Hide file tree
Showing 20 changed files with 716 additions and 21 deletions.
2 changes: 2 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ type Client interface {
// SetExternalTimestamp sets external timestamp
SetExternalTimestamp(ctx context.Context, timestamp uint64) error

// MetaStorageClient returns the meta storage client.
MetaStorageClient
// KeyspaceClient manages keyspace metadata.
KeyspaceClient
// ResourceManagerClient manages resource group metadata and token assignment.
Expand Down
2 changes: 1 addition & 1 deletion client/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/opentracing/opentracing-go v1.2.0
github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c
github.com/pingcap/failpoint v0.0.0-20210918120811-547c13e3eb00
github.com/pingcap/kvproto v0.0.0-20230220070831-5cc42e4327e4
github.com/pingcap/kvproto v0.0.0-20230228041042-1e9aca94bab6
github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3
github.com/prometheus/client_golang v1.11.1
github.com/stretchr/testify v1.8.1
Expand Down
4 changes: 2 additions & 2 deletions client/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c h1:xpW9bvK+HuuTm
github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
github.com/pingcap/failpoint v0.0.0-20210918120811-547c13e3eb00 h1:C3N3itkduZXDZFh4N3vQ5HEtld3S+Y+StULhWVvumU0=
github.com/pingcap/failpoint v0.0.0-20210918120811-547c13e3eb00/go.mod h1:4qGtCB0QK0wBzKtFEGDhxXnSnbQApw1gc9siScUl8ew=
github.com/pingcap/kvproto v0.0.0-20230220070831-5cc42e4327e4 h1:A3F8guzyZoU1vShtTnGwqW+ZK3RxGhfAf5EZJR8+mNQ=
github.com/pingcap/kvproto v0.0.0-20230220070831-5cc42e4327e4/go.mod h1:+on3Lfk/fb1lXkud3XvskJumhSIEEgN2TTbMObUlrxE=
github.com/pingcap/kvproto v0.0.0-20230228041042-1e9aca94bab6 h1:bgLRG7gPJCq6aduA65ZV7xWQBThTcuarBB9VdfAzV4g=
github.com/pingcap/kvproto v0.0.0-20230228041042-1e9aca94bab6/go.mod h1:KUrW1FGoznGMMTssYBu0czfAhn6vQcIrHyZoSC6T990=
github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3 h1:HR/ylkkLmGdSSDaD8IDP+SZrdhV1Kibl9KrHxJ9eciw=
github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
223 changes: 223 additions & 0 deletions client/meta_storage_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// Copyright 2023 TiKV Project Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pd

import (
"context"
"time"

"github.com/opentracing/opentracing-go"
"github.com/pingcap/errors"
"github.com/pingcap/kvproto/pkg/meta_storagepb"
"github.com/pingcap/log"
"github.com/prometheus/client_golang/prometheus"
"github.com/tikv/pd/client/grpcutil"
"go.uber.org/zap"
)

// MetaStorageClient is the interface for meta storage client.
type MetaStorageClient interface {
// Watch watches on a key or prefix.
Watch(ctx context.Context, key []byte, opts ...OpOption) (chan []*meta_storagepb.Event, error)
// Get gets the value for a key.
Get(ctx context.Context, key []byte, opts ...OpOption) (*meta_storagepb.GetResponse, error)
// Put puts a key-value pair into meta storage.
Put(ctx context.Context, key []byte, value []byte, opts ...OpOption) (*meta_storagepb.PutResponse, error)
}

// metaStorageClient gets the meta storage client from current PD leader.
func (c *client) metaStorageClient() meta_storagepb.MetaStorageClient {
if client := c.bc.GetServingEndpointClientConn(); client != nil {
return meta_storagepb.NewMetaStorageClient(client)
}
return nil
}

// Op represents available options when using meta storage client.
type Op struct {
rangeEnd []byte
revision int64
prevKv bool
lease int64
limit int64
isOptsWithPrefix bool
}

// OpOption configures etcd Op.
type OpOption func(*Op)

// WithLimit specifies the limit of the key.
func WithLimit(limit int64) OpOption {
return func(op *Op) { op.limit = limit }
}

// WithRangeEnd specifies the range end of the key.
func WithRangeEnd(rangeEnd []byte) OpOption {
return func(op *Op) { op.rangeEnd = rangeEnd }
}

// WithRev specifies the start revision of the key.
func WithRev(revision int64) OpOption {
return func(op *Op) { op.revision = revision }
}

// WithPrevKV specifies the previous key-value pair of the key.
func WithPrevKV() OpOption {
return func(op *Op) { op.prevKv = true }
}

// WithLease specifies the lease of the key.
func WithLease(lease int64) OpOption {
return func(op *Op) { op.lease = lease }
}

// WithPrefix specifies the prefix of the key.
func WithPrefix() OpOption {
return func(op *Op) {
op.isOptsWithPrefix = true
}
}

// See https://github.com/etcd-io/etcd/blob/da4bf0f76fb708e0b57763edb46ba523447b9510/client/v3/op.go#L372-L385
func getPrefix(key []byte) []byte {
end := make([]byte, len(key))
copy(end, key)
for i := len(end) - 1; i >= 0; i-- {
if end[i] < 0xff {
end[i]++
end = end[:i+1]
return end
}
}
return []byte{0}
}

func (c *client) Put(ctx context.Context, key, value []byte, opts ...OpOption) (*meta_storagepb.PutResponse, error) {
options := &Op{}
for _, opt := range opts {
opt(options)
}

if span := opentracing.SpanFromContext(ctx); span != nil {
span = opentracing.StartSpan("pdclient.Put", opentracing.ChildOf(span.Context()))
defer span.Finish()
}
start := time.Now()
defer func() { cmdDurationPut.Observe(time.Since(start).Seconds()) }()

ctx, cancel := context.WithTimeout(ctx, c.option.timeout)
req := &meta_storagepb.PutRequest{
Key: key,
Value: value,
Lease: options.lease,
PrevKv: options.prevKv,
}
ctx = grpcutil.BuildForwardContext(ctx, c.GetLeaderAddr())
resp, err := c.metaStorageClient().Put(ctx, req)
cancel()

if err = c.respForMetaStorageErr(cmdFailedDurationPut, start, err, resp.GetHeader()); err != nil {
return nil, err
}
return resp, nil
}

func (c *client) Get(ctx context.Context, key []byte, opts ...OpOption) (*meta_storagepb.GetResponse, error) {
options := &Op{}
for _, opt := range opts {
opt(options)
}
if options.isOptsWithPrefix {
options.rangeEnd = getPrefix(key)
}

if span := opentracing.SpanFromContext(ctx); span != nil {
span = opentracing.StartSpan("pdclient.Get", opentracing.ChildOf(span.Context()))
defer span.Finish()
}
start := time.Now()
defer func() { cmdDurationGet.Observe(time.Since(start).Seconds()) }()

ctx, cancel := context.WithTimeout(ctx, c.option.timeout)
req := &meta_storagepb.GetRequest{
Key: key,
RangeEnd: options.rangeEnd,
Limit: options.limit,
Revision: options.revision,
}
ctx = grpcutil.BuildForwardContext(ctx, c.GetLeaderAddr())
resp, err := c.metaStorageClient().Get(ctx, req)
cancel()

if err = c.respForMetaStorageErr(cmdFailedDurationGet, start, err, resp.GetHeader()); err != nil {
return nil, err
}
return resp, nil
}

func (c *client) Watch(ctx context.Context, key []byte, opts ...OpOption) (chan []*meta_storagepb.Event, error) {
eventCh := make(chan []*meta_storagepb.Event, 100)
options := &Op{}
for _, opt := range opts {
opt(options)
}
if options.isOptsWithPrefix {
options.rangeEnd = getPrefix(key)
}

res, err := c.metaStorageClient().Watch(ctx, &meta_storagepb.WatchRequest{
Key: key,
RangeEnd: options.rangeEnd,
StartRevision: options.revision,
PrevKv: options.prevKv,
})
if err != nil {
close(eventCh)
return nil, err
}
go func() {
defer func() {
close(eventCh)
if r := recover(); r != nil {
log.Error("[pd] panic in client `Watch`", zap.Any("error", r))
return
}
}()
for {
resp, err := res.Recv()
if err != nil {
return
}
select {
case <-ctx.Done():
return
case eventCh <- resp.GetEvents():
}
}
}()
return eventCh, err
}

func (c *client) respForMetaStorageErr(observer prometheus.Observer, start time.Time, err error, header *meta_storagepb.ResponseHeader) error {
if err != nil || header.GetError() != nil {
observer.Observe(time.Since(start).Seconds())
if err != nil {
c.bc.ScheduleCheckMemberChanged()
return errors.WithStack(err)
}
return errors.WithStack(errors.New(header.GetError().String()))
}
return nil
}
4 changes: 4 additions & 0 deletions client/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ var (
cmdDurationSplitAndScatterRegions = cmdDuration.WithLabelValues("split_and_scatter_regions")
cmdDurationLoadKeyspace = cmdDuration.WithLabelValues("load_keyspace")
cmdDurationUpdateKeyspaceState = cmdDuration.WithLabelValues("update_keyspace_state")
cmdDurationGet = cmdDuration.WithLabelValues("get")
cmdDurationPut = cmdDuration.WithLabelValues("put")

cmdFailDurationGetRegion = cmdFailedDuration.WithLabelValues("get_region")
cmdFailDurationTSO = cmdFailedDuration.WithLabelValues("tso")
Expand All @@ -115,6 +117,8 @@ var (
cmdFailedDurationLoadKeyspace = cmdDuration.WithLabelValues("load_keyspace")
cmdFailedDurationUpdateKeyspaceState = cmdDuration.WithLabelValues("update_keyspace_state")
requestDurationTSO = requestDuration.WithLabelValues("tso")
cmdFailedDurationGet = cmdFailedDuration.WithLabelValues("get")
cmdFailedDurationPut = cmdFailedDuration.WithLabelValues("put")
)

func init() {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require (
github.com/pingcap/errcode v0.3.0
github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c
github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce
github.com/pingcap/kvproto v0.0.0-20230216063518-fe71e5de4643
github.com/pingcap/kvproto v0.0.0-20230228041042-1e9aca94bab6
github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3
github.com/pingcap/sysutil v0.0.0-20211208032423-041a72e5860d
github.com/pingcap/tidb-dashboard v0.0.0-20230209052558-a58fc2a7e924
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,8 @@ github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c/go.mod h1:X2r9ue
github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce h1:Y1kCxlCtlPTMtVcOkjUcuQKh+YrluSo7+7YMCQSzy30=
github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce/go.mod h1:w4PEZ5y16LeofeeGwdgZB4ddv9bLyDuIX+ljstgKZyk=
github.com/pingcap/kvproto v0.0.0-20191211054548-3c6b38ea5107/go.mod h1:WWLmULLO7l8IOcQG+t+ItJ3fEcrL5FxF0Wu+HrMy26w=
github.com/pingcap/kvproto v0.0.0-20230216063518-fe71e5de4643 h1:t+p7AGGpNSDu0mm0DqSrDts79/JRSDo3rdgWjhIg5eQ=
github.com/pingcap/kvproto v0.0.0-20230216063518-fe71e5de4643/go.mod h1:+on3Lfk/fb1lXkud3XvskJumhSIEEgN2TTbMObUlrxE=
github.com/pingcap/kvproto v0.0.0-20230228041042-1e9aca94bab6 h1:bgLRG7gPJCq6aduA65ZV7xWQBThTcuarBB9VdfAzV4g=
github.com/pingcap/kvproto v0.0.0-20230228041042-1e9aca94bab6/go.mod h1:KUrW1FGoznGMMTssYBu0czfAhn6vQcIrHyZoSC6T990=
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7/go.mod h1:8AanEdAHATuRurdGxZXBz0At+9avep+ub7U1AGYLIMM=
github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3 h1:HR/ylkkLmGdSSDaD8IDP+SZrdhV1Kibl9KrHxJ9eciw=
Expand Down
6 changes: 3 additions & 3 deletions pkg/mcs/discovery/discover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ func TestDiscover(t *testing.T) {
re.NoError(err)

ep := cfg.LCUrls[0].String()
client, err := clientv3.New(clientv3.Config{
Endpoints: []string{ep},
})
re.NoError(err)

client, err := clientv3.NewFromURL(ep)
re.NoError(err)

<-etcd.Server.ReadyNotify()
Expand Down
6 changes: 3 additions & 3 deletions pkg/mcs/discovery/register_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ func TestRegister(t *testing.T) {
re.NoError(err)

ep := cfg.LCUrls[0].String()
client, err := clientv3.New(clientv3.Config{
Endpoints: []string{ep},
})
client, err := clientv3.NewFromURL(ep)
re.NoError(err)

<-etcd.Server.ReadyNotify()
sr := NewServiceRegister(context.Background(), client, "test_service", "127.0.0.1:1", "127.0.0.1:1", 10)
re.NoError(err)
err = sr.Register()
re.NoError(err)
resp, err := client.Get(context.Background(), sr.key)
Expand All @@ -55,6 +54,7 @@ func TestRegister(t *testing.T) {
re.Empty(resp.Kvs)

sr = NewServiceRegister(context.Background(), client, "test_service", "127.0.0.1:2", "127.0.0.1:2", 1)
re.NoError(err)
err = sr.Register()
re.NoError(err)
sr.cancel()
Expand Down
Loading

0 comments on commit f999ad5

Please sign in to comment.