You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ti...@apache.org on 2019/09/16 09:47:34 UTC

[servicecomb-service-center] branch master updated: Bugfix: data decreasing causes panic during paging (#587)

This is an automated email from the ASF dual-hosted git repository.

tianxiaoliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new c655323  Bugfix: data decreasing causes panic during paging (#587)
c655323 is described below

commit c655323f1c4b10cde53fda2a862c280d3581e1f5
Author: humingcheng <hu...@163.com>
AuthorDate: Mon Sep 16 17:47:29 2019 +0800

    Bugfix: data decreasing causes panic during paging (#587)
---
 server/plugin/pkg/registry/etcd/etcd.go      |  13 ++-
 server/plugin/pkg/registry/etcd/etcd_test.go | 118 ++++++++++++++++++++++++---
 2 files changed, 115 insertions(+), 16 deletions(-)

diff --git a/server/plugin/pkg/registry/etcd/etcd.go b/server/plugin/pkg/registry/etcd/etcd.go
index 2f872ca..04344ce 100644
--- a/server/plugin/pkg/registry/etcd/etcd.go
+++ b/server/plugin/pkg/registry/etcd/etcd.go
@@ -20,6 +20,11 @@ import (
 	"crypto/tls"
 	"errors"
 	"fmt"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
 	"github.com/apache/servicecomb-service-center/pkg/backoff"
 	errorsEx "github.com/apache/servicecomb-service-center/pkg/errors"
 	"github.com/apache/servicecomb-service-center/pkg/gopool"
@@ -28,15 +33,12 @@ import (
 	"github.com/apache/servicecomb-service-center/server/alarm"
 	mgr "github.com/apache/servicecomb-service-center/server/plugin"
 	"github.com/apache/servicecomb-service-center/server/plugin/pkg/registry"
+
 	"github.com/coreos/etcd/clientv3"
 	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
 	"github.com/coreos/etcd/etcdserver/etcdserverpb"
 	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
-	"net/url"
-	"strconv"
-	"strings"
-	"time"
 )
 
 var firstEndpoint string
@@ -419,6 +421,9 @@ func (c *EtcdClient) paging(ctx context.Context, op registry.PluginOp) (*clientv
 		}
 
 		l := int64(len(recordResp.Kvs))
+		if l <= 0 { // no more data, data may decrease during paging
+			break
+		}
 		nextKey = util.BytesToStringWithNoCopy(recordResp.Kvs[l-1].Key)
 		if i < minPage {
 			// even through current page index less then the min page index,
diff --git a/server/plugin/pkg/registry/etcd/etcd_test.go b/server/plugin/pkg/registry/etcd/etcd_test.go
index aa4e69f..988c0ce 100644
--- a/server/plugin/pkg/registry/etcd/etcd_test.go
+++ b/server/plugin/pkg/registry/etcd/etcd_test.go
@@ -20,19 +20,24 @@ import _ "github.com/apache/servicecomb-service-center/server/plugin/pkg/tracing
 import _ "github.com/apache/servicecomb-service-center/server/plugin/pkg/security/buildin"
 import _ "github.com/apache/servicecomb-service-center/server/plugin/pkg/tls/buildin"
 import (
+	context2 "context"
 	"fmt"
-	"github.com/apache/servicecomb-service-center/server/core"
-	"github.com/apache/servicecomb-service-center/server/plugin/pkg/registry"
-	"github.com/apache/servicecomb-service-center/server/rpc"
-	"golang.org/x/net/context"
-	"google.golang.org/grpc/status"
-	"net/http"
 	"os"
 	"strconv"
 	"strings"
 	"sync"
 	"testing"
 	"time"
+
+	"github.com/apache/servicecomb-service-center/server/core"
+	"github.com/apache/servicecomb-service-center/server/plugin/pkg/registry"
+	"github.com/apache/servicecomb-service-center/server/rpc"
+
+	"github.com/coreos/etcd/clientv3"
+	"github.com/coreos/etcd/etcdserver/etcdserverpb"
+	"github.com/coreos/etcd/mvcc/mvccpb"
+	"golang.org/x/net/context"
+	"google.golang.org/grpc/status"
 )
 
 const (
@@ -746,6 +751,101 @@ func TestEtcdClient_Watch(t *testing.T) {
 	<-ch
 }
 
+type mockKVForPagine struct {
+	rangeCount int
+	countResp  *clientv3.GetResponse
+	rangeResp1 *clientv3.GetResponse
+	rangeResp2 *clientv3.GetResponse
+}
+
+func (m *mockKVForPagine) Put(ctx context2.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) {
+	return nil, nil
+}
+
+func (m *mockKVForPagine) Get(ctx context2.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
+	op := &clientv3.Op{}
+	for _, o := range opts {
+		o(op)
+	}
+	if op.IsCountOnly() {
+		return m.countResp, nil
+	}
+	if m.rangeCount == 0 {
+		m.rangeCount = 1
+		return m.rangeResp1, nil
+	}
+	return m.rangeResp2, nil
+}
+
+func (m *mockKVForPagine) Delete(ctx context2.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
+	return nil, nil
+}
+
+func (m *mockKVForPagine) Compact(ctx context2.Context, rev int64, opts ...clientv3.CompactOption) (*clientv3.CompactResponse, error) {
+	return nil, nil
+}
+
+func (m *mockKVForPagine) Do(ctx context2.Context, op clientv3.Op) (clientv3.OpResponse, error) {
+	return clientv3.OpResponse{}, nil
+}
+
+func (m *mockKVForPagine) Txn(ctx context2.Context) clientv3.Txn {
+	return nil
+}
+
+// test scenario: db data decreases during paging.
+func TestEtcdClient_paging(t *testing.T) {
+	// key range: [startKey, endKey)
+	generateGetResp := func(startKey, endKey int) *clientv3.GetResponse {
+		resp := &clientv3.GetResponse{
+			Count: int64(endKey - startKey),
+			Header: &etcdserverpb.ResponseHeader{
+				Revision: 0,
+			},
+			Kvs: make([]*mvccpb.KeyValue, 0),
+		}
+		if resp.Count <= 0 {
+			return resp
+		}
+		for i := startKey; i < endKey; i++ {
+			kvPart := &mvccpb.KeyValue{
+				Key:   []byte(fmt.Sprint(i)),
+				Value: []byte(""),
+			}
+			resp.Kvs = append(resp.Kvs, kvPart)
+		}
+		return resp
+	}
+
+	mockKv := &mockKVForPagine{
+		rangeCount: 0,
+		// if count only, return 4097 kvs
+		countResp: generateGetResp(0, 4097),
+		// the first paging request, return 4096 kvs
+		rangeResp1: generateGetResp(0, 4096),
+		// the second paging request, return 0 kv
+		// meaning data decreases during paging
+		rangeResp2: generateGetResp(0, 0),
+	}
+	c := EtcdClient{
+		Client: &clientv3.Client{
+			KV: mockKv,
+		},
+	}
+
+	op := registry.PluginOp{
+		Offset: -1,
+		Limit:  registry.DEFAULT_PAGE_COUNT,
+	}
+	r, err := c.paging(context2.Background(), op)
+	if err != nil {
+		t.Fatalf("TestEtcdClient_paging failed, %#v", err)
+	}
+	if len(r.Kvs) <= 0 {
+		t.Fatalf("TestEtcdClient_paging failed")
+	}
+}
+
 func TestNewRegistry(t *testing.T) {
 	etcd := &EtcdClient{
 		Endpoints:        []string{endpoint, "0.0.0.0:2379"},
@@ -759,12 +859,6 @@ func TestNewRegistry(t *testing.T) {
 	}
 }
 
-type mockTLSHandler struct {
-}
-
-func (m *mockTLSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-}
-
 func TestWithTLS(t *testing.T) {
 	sslRoot := "../../../../../examples/service_center/ssl/"
 	os.Setenv("SSL_ROOT", sslRoot)