From 4fc89678b205b563414aab1640cd6eb0166790ba Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Thu, 18 Feb 2016 11:16:11 -0800 Subject: [PATCH] etcdserver: add >= support for v3 delete range --- clientv3/integration/kv_test.go | 68 ++++++++++++++++++++++++--------- etcdserver/v3demo_server.go | 14 +++++-- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/clientv3/integration/kv_test.go b/clientv3/integration/kv_test.go index f393f5365..91779ec69 100644 --- a/clientv3/integration/kv_test.go +++ b/clientv3/integration/kv_test.go @@ -229,35 +229,65 @@ func TestKVDeleteRange(t *testing.T) { kv := clientv3.NewKV(clus.RandClient()) ctx := context.TODO() - keySet := []string{"a", "b", "c", "c", "c", "d", "e", "f"} - for i, key := range keySet { - if _, err := kv.Put(ctx, key, ""); err != nil { - t.Fatalf("#%d: couldn't put %q (%v)", i, key, err) - } - } - tests := []struct { - key, end string - delRev int64 + key string + opts []clientv3.OpOption + + wkeys []string }{ - {"a", "b", int64(len(keySet) + 2)}, // delete [a, b) - {"d", "f", int64(len(keySet) + 3)}, // delete [d, f) + // [a, c) + { + key: "a", + opts: []clientv3.OpOption{clientv3.WithRange("c")}, + + wkeys: []string{"c", "c/abc", "d"}, + }, + // >= c + { + key: "c", + opts: []clientv3.OpOption{clientv3.WithFromKey()}, + + wkeys: []string{"a", "b"}, + }, + // c* + { + key: "c", + opts: []clientv3.OpOption{clientv3.WithPrefix()}, + + wkeys: []string{"a", "b", "d"}, + }, + // * + { + key: "\x00", + opts: []clientv3.OpOption{clientv3.WithFromKey()}, + + wkeys: []string{}, + }, } for i, tt := range tests { - dresp, err := kv.Delete(ctx, tt.key, clientv3.WithRange(tt.end)) + keySet := []string{"a", "b", "c", "c/abc", "d"} + for j, key := range keySet { + if _, err := kv.Put(ctx, key, ""); err != nil { + t.Fatalf("#%d: couldn't put %q (%v)", j, key, err) + } + } + + _, err := kv.Delete(ctx, tt.key, tt.opts...) if err != nil { t.Fatalf("#%d: couldn't delete range (%v)", i, err) } - if dresp.Header.Revision != tt.delRev { - t.Fatalf("#%d: dresp.Header.Revision got %d, want %d", i, dresp.Header.Revision, tt.delRev) - } - resp, err := kv.Get(ctx, tt.key, clientv3.WithRange(tt.end)) + + resp, err := kv.Get(ctx, "a", clientv3.WithFromKey()) if err != nil { - t.Fatalf("#%d: couldn't get key (%v)", i, err) + t.Fatalf("#%d: couldn't get keys (%v)", i, err) } - if len(resp.Kvs) > 0 { - t.Fatalf("#%d: resp.Kvs expected none, but got %+v", i, resp.Kvs) + keys := []string{} + for _, kv := range resp.Kvs { + keys = append(keys, string(kv.Key)) + } + if !reflect.DeepEqual(tt.wkeys, keys) { + t.Errorf("#%d: resp.Kvs got %v, expected %v", i, keys, tt.wkeys) } } } diff --git a/etcdserver/v3demo_server.go b/etcdserver/v3demo_server.go index a05d882b2..5b41f96c4 100644 --- a/etcdserver/v3demo_server.go +++ b/etcdserver/v3demo_server.go @@ -319,9 +319,7 @@ func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeRespo err error ) - // grpc sends empty byte strings as nils, so use a '\0' to indicate - // wanting a >= query - if len(r.RangeEnd) == 1 && r.RangeEnd[0] == 0 { + if isGteRange(r.RangeEnd) { r.RangeEnd = []byte{} } @@ -390,6 +388,10 @@ func applyDeleteRange(txnID int64, kv dstorage.KV, dr *pb.DeleteRangeRequest) (* err error ) + if isGteRange(dr.RangeEnd) { + dr.RangeEnd = []byte{} + } + if txnID != noTxn { _, rev, err = kv.TxnDeleteRange(txnID, dr.Key, dr.RangeEnd) if err != nil { @@ -631,3 +633,9 @@ func compareInt64(a, b int64) int { return 0 } } + +// isGteRange determines if the range end is a >= range. This works around grpc +// sending empty byte strings as nil; >= is encoded in the range end as '\0'. +func isGteRange(rangeEnd []byte) bool { + return len(rangeEnd) == 1 && rangeEnd[0] == 0 +}