Browse Source

Merge pull request #4224 from heyitsanthony/v3-rangereq-more-flag

support V3 rangereq more flag and revision request flag
Anthony Romano 10 years ago
parent
commit
9ca3c8e581
2 changed files with 183 additions and 5 deletions
  1. 11 5
      etcdserver/v3demo_server.go
  2. 172 0
      integration/v3_grpc_test.go

+ 11 - 5
etcdserver/v3demo_server.go

@@ -246,14 +246,18 @@ func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeRespo
 		// fetch everything; sort and truncate afterwards
 		limit = 0
 	}
+	if limit > 0 {
+		// fetch one extra for 'more' flag
+		limit = limit + 1
+	}
 
 	if txnID != noTxn {
-		kvs, rev, err = kv.TxnRange(txnID, r.Key, r.RangeEnd, limit, 0)
+		kvs, rev, err = kv.TxnRange(txnID, r.Key, r.RangeEnd, limit, r.Revision)
 		if err != nil {
 			return nil, err
 		}
 	} else {
-		kvs, rev, err = kv.Range(r.Key, r.RangeEnd, limit, 0)
+		kvs, rev, err = kv.Range(r.Key, r.RangeEnd, limit, r.Revision)
 		if err != nil {
 			return nil, err
 		}
@@ -279,9 +283,11 @@ func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeRespo
 		case r.SortOrder == pb.RangeRequest_DESCEND:
 			sort.Sort(sort.Reverse(sorter))
 		}
-		if r.Limit > 0 && len(kvs) > int(r.Limit) {
-			kvs = kvs[:r.Limit]
-		}
+	}
+
+	if r.Limit > 0 && len(kvs) > int(r.Limit) {
+		kvs = kvs[:r.Limit]
+		resp.More = true
 	}
 
 	resp.Header.Revision = rev

+ 172 - 0
integration/v3_grpc_test.go

@@ -582,3 +582,175 @@ func WaitResponse(wc pb.Watch_WatchClient, timeout time.Duration) (bool, *pb.Wat
 	}
 	return true, nil
 }
+
+func TestV3RangeRequest(t *testing.T) {
+	tests := []struct {
+		putKeys []string
+		reqs    []pb.RangeRequest
+
+		wresps [][]string
+		wmores []bool
+	}{
+		// single key
+		{
+			[]string{"foo", "bar"},
+			[]pb.RangeRequest{
+				// exists
+				{Key: []byte("foo")},
+				// doesn't exist
+				{Key: []byte("baz")},
+			},
+
+			[][]string{
+				{"foo"},
+				{},
+			},
+			[]bool{false, false},
+		},
+		// multi-key
+		{
+			[]string{"a", "b", "c", "d", "e"},
+			[]pb.RangeRequest{
+				// all in range
+				{Key: []byte("a"), RangeEnd: []byte("z")},
+				// [b, d)
+				{Key: []byte("b"), RangeEnd: []byte("d")},
+				// out of range
+				{Key: []byte("f"), RangeEnd: []byte("z")},
+				// [c,c) = empty
+				{Key: []byte("c"), RangeEnd: []byte("c")},
+				// [d, b) = empty
+				{Key: []byte("d"), RangeEnd: []byte("b")},
+			},
+
+			[][]string{
+				{"a", "b", "c", "d", "e"},
+				{"b", "c"},
+				{},
+				{},
+				{},
+			},
+			[]bool{false, false, false, false, false},
+		},
+		// revision
+		{
+			[]string{"a", "b", "c", "d", "e"},
+			[]pb.RangeRequest{
+				{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 0},
+				{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 1},
+				{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 2},
+				{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 3},
+			},
+
+			[][]string{
+				{"a", "b", "c", "d", "e"},
+				{},
+				{"a"},
+				{"a", "b"},
+			},
+			[]bool{false, false, false, false},
+		},
+		// limit
+		{
+			[]string{"foo", "bar"},
+			[]pb.RangeRequest{
+				// more
+				{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 1},
+				// no more
+				{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 2},
+			},
+
+			[][]string{
+				{"bar"},
+				{"bar", "foo"},
+			},
+			[]bool{true, false},
+		},
+		// sort
+		{
+			[]string{"b", "a", "c", "d", "c"},
+			[]pb.RangeRequest{
+				{
+					Key: []byte("a"), RangeEnd: []byte("z"),
+					Limit:      1,
+					SortOrder:  pb.RangeRequest_ASCEND,
+					SortTarget: pb.RangeRequest_KEY,
+				},
+				{
+					Key: []byte("a"), RangeEnd: []byte("z"),
+					Limit:      1,
+					SortOrder:  pb.RangeRequest_DESCEND,
+					SortTarget: pb.RangeRequest_KEY,
+				},
+				{
+					Key: []byte("a"), RangeEnd: []byte("z"),
+					Limit:      1,
+					SortOrder:  pb.RangeRequest_ASCEND,
+					SortTarget: pb.RangeRequest_CREATE,
+				},
+				{
+					Key: []byte("a"), RangeEnd: []byte("z"),
+					Limit:      1,
+					SortOrder:  pb.RangeRequest_DESCEND,
+					SortTarget: pb.RangeRequest_MOD,
+				},
+				{
+					Key: []byte("z"), RangeEnd: []byte("z"),
+					Limit:      1,
+					SortOrder:  pb.RangeRequest_DESCEND,
+					SortTarget: pb.RangeRequest_CREATE,
+				},
+			},
+
+			[][]string{
+				{"a"},
+				{"d"},
+				{"b"},
+				{"c"},
+				{},
+			},
+			[]bool{true, true, true, true, false},
+		},
+	}
+
+	for i, tt := range tests {
+		clus := newClusterGRPC(t, &clusterConfig{size: 3})
+		for _, k := range tt.putKeys {
+			kvc := pb.NewKVClient(clus.RandConn())
+			req := &pb.PutRequest{Key: []byte(k), Value: []byte("bar")}
+			if _, err := kvc.Put(context.TODO(), req); err != nil {
+				t.Fatalf("#%d: couldn't put key (%v)", i, err)
+			}
+		}
+
+		for j, req := range tt.reqs {
+			kvc := pb.NewKVClient(clus.RandConn())
+			resp, err := kvc.Range(context.TODO(), &req)
+			if err != nil {
+				t.Errorf("#%d.%d: Range error: %v", i, j, err)
+				continue
+			}
+			if len(resp.Kvs) != len(tt.wresps[j]) {
+				t.Errorf("#%d.%d: bad len(resp.Kvs). got = %d, want = %d, ", i, j, len(resp.Kvs), len(tt.wresps[j]))
+				continue
+			}
+			for k, wKey := range tt.wresps[j] {
+				respKey := string(resp.Kvs[k].Key)
+				if respKey != wKey {
+					t.Errorf("#%d.%d: key[%d]. got = %v, want = %v, ", i, j, k, respKey, wKey)
+				}
+			}
+			if resp.More != tt.wmores[j] {
+				t.Errorf("#%d.%d: bad more. got = %v, want = %v, ", i, j, resp.More, tt.wmores[j])
+			}
+			wrev := req.Revision
+			if wrev == 0 {
+				wrev = int64(len(tt.putKeys) + 1)
+			}
+			if resp.Header.Revision != wrev {
+				t.Errorf("#%d.%d: bad header revision. got = %d. want = %d", i, j, resp.Header.Revision, wrev)
+			}
+		}
+		clus.Terminate(t)
+	}
+}