func rangeFunc(cmd *cobra.Command, args []string) { if len(args) == 0 || len(args) > 2 { fmt.Fprintln(os.Stderr, cmd.Usage()) os.Exit(1) } k := args[0] end := "" if len(args) == 2 { end = args[1] } if rangeConsistency == "l" { fmt.Println("bench with linearizable range") } else if rangeConsistency == "s" { fmt.Println("bench with serializable range") } else { fmt.Fprintln(os.Stderr, cmd.Usage()) os.Exit(1) } requests := make(chan v3.Op, totalClients) clients := mustCreateClients(totalClients, totalConns) bar = pb.New(rangeTotal) bar.Format("Bom !") bar.Start() r := newReport() for i := range clients { wg.Add(1) go func(c *v3.Client) { defer wg.Done() for op := range requests { st := time.Now() _, err := c.Do(context.Background(), op) r.Results() <- report.Result{Err: err, Start: st, End: time.Now()} bar.Increment() } }(clients[i]) } go func() { for i := 0; i < rangeTotal; i++ { opts := []v3.OpOption{v3.WithRange(end)} if rangeConsistency == "s" { opts = append(opts, v3.WithSerializable()) } op := v3.OpGet(k, opts...) requests <- op } close(requests) }() rc := r.Run() wg.Wait() close(r.Results()) bar.Finish() fmt.Printf("%s", <-rc) }
func newWatchBroadcast(wp *watchProxy, w *watcher, update func(*watchBroadcast)) *watchBroadcast { cctx, cancel := context.WithCancel(wp.ctx) wb := &watchBroadcast{ cancel: cancel, nextrev: w.nextrev, receivers: make(map[*watcher]struct{}), donec: make(chan struct{}), } wb.add(w) go func() { defer close(wb.donec) // loop because leader loss will close channel for cctx.Err() == nil { wch := wp.cw.Watch(cctx, w.wr.key, clientv3.WithRange(w.wr.end), clientv3.WithProgressNotify(), clientv3.WithCreatedNotify(), clientv3.WithRev(wb.nextrev), clientv3.WithPrevKV(), ) for wr := range wch { wb.bcast(wr) update(wb) } wp.retryLimiter.Wait(cctx) } }() return wb }
func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) { if len(args) == 0 || len(args) > 2 { ExitWithError(ExitBadArgs, fmt.Errorf("del command needs one argument as key and an optional argument as range_end.")) } if delPrefix && delFromKey { ExitWithError(ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one.")) } opts := []clientv3.OpOption{} key := args[0] if len(args) > 1 { if delPrefix || delFromKey { ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set.")) } opts = append(opts, clientv3.WithRange(args[1])) } if delPrefix { opts = append(opts, clientv3.WithPrefix()) } if delPrevKV { opts = append(opts, clientv3.WithPrevKV()) } if delFromKey { if len(key) == 0 { key = "\x00" } opts = append(opts, clientv3.WithFromKey()) } return key, opts }
// watchCommandFunc executes the "watch" command. func watchCommandFunc(cmd *cobra.Command, args []string) { if watchInteractive { watchInteractiveFunc(cmd, args) return } if len(args) < 1 || len(args) > 2 { ExitWithError(ExitBadArgs, fmt.Errorf("watch in non-interactive mode requires one or two arguments as key or prefix, with range end")) } opts := []clientv3.OpOption{clientv3.WithRev(watchRev)} key := args[0] if len(args) == 2 { if watchPrefix { ExitWithError(ExitBadArgs, fmt.Errorf("`range_end` and `--prefix` cannot be set at the same time, choose one")) } opts = append(opts, clientv3.WithRange(args[1])) } if watchPrefix { opts = append(opts, clientv3.WithPrefix()) } c := mustClientFromCmd(cmd) wc := c.Watch(context.TODO(), key, opts...) printWatchCh(wc) err := c.Close() if err == nil { ExitWithError(ExitInterrupted, fmt.Errorf("watch is canceled by the server")) } ExitWithError(ExitBadConnection, err) }
func DelRequestToOp(r *pb.DeleteRangeRequest) clientv3.Op { opts := []clientv3.OpOption{} if len(r.RangeEnd) != 0 { opts = append(opts, clientv3.WithRange(string(r.RangeEnd))) } return clientv3.OpDelete(string(r.Key), opts...) }
func testWatchRange(t *testing.T, wctx *watchctx) { if wctx.ch = wctx.w.Watch(context.TODO(), "a", clientv3.WithRange("c")); wctx.ch == nil { t.Fatalf("expected non-nil channel") } putAndWatch(t, wctx, "a", "a") putAndWatch(t, wctx, "b", "b") putAndWatch(t, wctx, "bar", "bar") }
func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error) { respchan := make(chan clientv3.GetResponse, 1024) errchan := make(chan error, 1) // if rev is not specified, we will choose the most recent revision. if s.rev == 0 { resp, err := s.c.Get(ctx, "foo") if err != nil { errchan <- err close(respchan) close(errchan) return respchan, errchan } s.rev = resp.Header.Revision } go func() { defer close(respchan) defer close(errchan) var key string opts := []clientv3.OpOption{clientv3.WithLimit(batchLimit), clientv3.WithRev(s.rev)} if len(s.prefix) == 0 { // If len(s.prefix) == 0, we will sync the entire key-value space. // We then range from the smallest key (0x00) to the end. opts = append(opts, clientv3.WithFromKey()) key = "\x00" } else { // If len(s.prefix) != 0, we will sync key-value space with given prefix. // We then range from the prefix to the next prefix if exists. Or we will // range from the prefix to the end if the next prefix does not exists. opts = append(opts, clientv3.WithRange(clientv3.GetPrefixRangeEnd(s.prefix))) key = s.prefix } for { resp, err := s.c.Get(ctx, key, opts...) if err != nil { errchan <- err return } respchan <- (clientv3.GetResponse)(*resp) if !resp.More { return } // move to next key key = string(append(resp.Kvs[len(resp.Kvs)-1].Key, 0)) } }() return respchan, errchan }
func rangeFunc(cmd *cobra.Command, args []string) { if len(args) == 0 || len(args) > 2 { fmt.Fprintln(os.Stderr, cmd.Usage()) os.Exit(1) } k := args[0] end := "" if len(args) == 2 { end = args[1] } if rangeConsistency == "l" { fmt.Println("bench with linearizable range") } else if rangeConsistency == "s" { fmt.Println("bench with serializable range") } else { fmt.Fprintln(os.Stderr, cmd.Usage()) os.Exit(1) } results = make(chan result) requests := make(chan v3.Op, totalClients) bar = pb.New(rangeTotal) clients := mustCreateClients(totalClients, totalConns) bar.Format("Bom !") bar.Start() for i := range clients { wg.Add(1) go doRange(clients[i].KV, requests) } pdoneC := printReport(results) go func() { for i := 0; i < rangeTotal; i++ { opts := []v3.OpOption{v3.WithRange(end)} if rangeConsistency == "s" { opts = append(opts, v3.WithSerializable()) } op := v3.OpGet(k, opts...) requests <- op } close(requests) }() wg.Wait() bar.Finish() close(results) <-pdoneC }
func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) { if len(args) == 0 || len(args) > 2 { ExitWithError(ExitBadArgs, fmt.Errorf("del command needs one argument as key and an optional argument as range_end.")) } opts := []clientv3.OpOption{} key := args[0] if len(args) > 1 { opts = append(opts, clientv3.WithRange(args[1])) } return key, opts }
func watchInteractiveFunc(cmd *cobra.Command, args []string) { c := mustClientFromCmd(cmd) reader := bufio.NewReader(os.Stdin) for { l, err := reader.ReadString('\n') if err != nil { ExitWithError(ExitInvalidInput, fmt.Errorf("Error reading watch request line: %v", err)) } l = strings.TrimSuffix(l, "\n") args := argify(l) if len(args) < 2 { fmt.Fprintf(os.Stderr, "Invalid command %s (command type or key is not provided)\n", l) continue } if args[0] != "watch" { fmt.Fprintf(os.Stderr, "Invalid command %s (only support watch)\n", l) continue } flagset := NewWatchCommand().Flags() err = flagset.Parse(args[1:]) if err != nil { fmt.Fprintf(os.Stderr, "Invalid command %s (%v)\n", l, err) continue } moreargs := flagset.Args() if len(moreargs) < 1 || len(moreargs) > 2 { fmt.Fprintf(os.Stderr, "Invalid command %s (Too few or many arguments)\n", l) continue } var key string _, err = fmt.Sscanf(moreargs[0], "%q", &key) if err != nil { key = moreargs[0] } opts := []clientv3.OpOption{clientv3.WithRev(watchRev)} if len(moreargs) == 2 { if watchPrefix { fmt.Fprintf(os.Stderr, "`range_end` and `--prefix` cannot be set at the same time, choose one\n") continue } opts = append(opts, clientv3.WithRange(moreargs[1])) } if watchPrefix { opts = append(opts, clientv3.WithPrefix()) } ch := c.Watch(context.TODO(), key, opts...) go printWatchCh(ch) } }
func TestKVDeleteRange(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(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 }{ {"a", "b", int64(len(keySet) + 2)}, // delete [a, b) {"d", "f", int64(len(keySet) + 3)}, // delete [d, f) } for i, tt := range tests { dresp, err := kv.Delete(ctx, tt.key, clientv3.WithRange(tt.end)) 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)) if err != nil { t.Fatalf("#%d: couldn't get key (%v)", i, err) } if len(resp.Kvs) > 0 { t.Fatalf("#%d: resp.Kvs expected none, but got %+v", i, resp.Kvs) } } }
func (sws *serverWatchStream) addDedicatedWatcher(w watcher, rev int64) { sws.mu.Lock() defer sws.mu.Unlock() ctx, cancel := context.WithCancel(context.Background()) wch := sws.c.Watch(ctx, w.wr.key, clientv3.WithRange(w.wr.end), clientv3.WithRev(rev), clientv3.WithProgressNotify(), ) ws := newWatcherSingle(wch, cancel, w, sws) sws.singles[w.id] = ws go ws.run() }
func (wgs *watchergroups) addWatcher(rid receiverID, w watcher) { wgs.mu.Lock() defer wgs.mu.Unlock() groups := wgs.groups if wg, ok := groups[w.wr]; ok { rev := wg.add(rid, w) wgs.idToGroup[rid] = wg if rev == 0 { // The group is newly created, the create event has not been delivered // to this group yet. // We can rely on etcd server to deliver the create event. // Or we might end up sending created event twice. return } resp := &pb.WatchResponse{ Header: &pb.ResponseHeader{ // todo: fill in ClusterId // todo: fill in MemberId: Revision: rev, // todo: fill in RaftTerm: }, WatchId: rid.watcherID, Created: true, } w.ch <- resp return } ctx, cancel := context.WithCancel(wgs.proxyCtx) wch := wgs.cw.Watch(ctx, w.wr.key, clientv3.WithRange(w.wr.end), clientv3.WithProgressNotify(), clientv3.WithCreatedNotify(), ) watchg := newWatchergroup(wch, cancel) watchg.add(rid, w) go watchg.run() groups[w.wr] = watchg wgs.idToGroup[rid] = watchg }
func getGetOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) { if len(args) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("range command needs arguments.")) } opts := []clientv3.OpOption{} key := args[0] if len(args) > 1 { opts = append(opts, clientv3.WithRange(args[1])) } opts = append(opts, clientv3.WithLimit(getLimit)) sortByOrder := clientv3.SortNone sortOrder := strings.ToUpper(getSortOrder) switch { case sortOrder == "ASCEND": sortByOrder = clientv3.SortAscend case sortOrder == "DESCEND": sortByOrder = clientv3.SortDescend case sortOrder == "": // nothing default: ExitWithError(ExitBadFeature, fmt.Errorf("bad sort order %v", getSortOrder)) } sortByTarget := clientv3.SortByKey sortTarget := strings.ToUpper(getSortTarget) switch { case sortTarget == "CREATE": sortByTarget = clientv3.SortByCreatedRev case sortTarget == "KEY": sortByTarget = clientv3.SortByKey case sortTarget == "MODIFY": sortByTarget = clientv3.SortByModifiedRev case sortTarget == "VALUE": sortByTarget = clientv3.SortByValue case sortTarget == "VERSION": sortByTarget = clientv3.SortByVersion case sortTarget == "": // nothing default: ExitWithError(ExitBadFeature, fmt.Errorf("bad sort target %v", getSortTarget)) } opts = append(opts, clientv3.WithSort(sortByTarget, sortByOrder)) return key, opts }
func RangeRequestToOp(r *pb.RangeRequest) clientv3.Op { opts := []clientv3.OpOption{} if len(r.RangeEnd) != 0 { opts = append(opts, clientv3.WithRange(string(r.RangeEnd))) } opts = append(opts, clientv3.WithRev(r.Revision)) opts = append(opts, clientv3.WithLimit(r.Limit)) opts = append(opts, clientv3.WithSort( clientv3.SortTarget(r.SortTarget), clientv3.SortOrder(r.SortOrder)), ) if r.Serializable { opts = append(opts, clientv3.WithSerializable()) } return clientv3.OpGet(string(r.Key), opts...) }
func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) { if len(args) == 0 || len(args) > 2 { ExitWithError(ExitBadArgs, fmt.Errorf("del command needs one argument as key and an optional argument as range_end.")) } opts := []clientv3.OpOption{} key := args[0] if len(args) > 1 { if delPrefix { ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one arguement when `--prefix` is set.")) } opts = append(opts, clientv3.WithRange(args[1])) } if delPrefix { opts = append(opts, clientv3.WithPrefix()) } return key, opts }
func (wgs *watchergroups) addWatcher(rid receiverID, w watcher) { wgs.mu.Lock() defer wgs.mu.Unlock() groups := wgs.groups if wg, ok := groups[w.wr]; ok { wg.add(rid, w) return } ctx, cancel := context.WithCancel(context.Background()) wch := wgs.cw.Watch(ctx, w.wr.key, clientv3.WithRange(w.wr.end), clientv3.WithProgressNotify()) watchg := newWatchergroup(wch, cancel) watchg.add(rid, w) go watchg.run() groups[w.wr] = watchg }
func getWatchChan(c *clientv3.Client, args []string) (clientv3.WatchChan, error) { if len(args) < 1 || len(args) > 2 { return nil, fmt.Errorf("bad number of arguments") } key := args[0] opts := []clientv3.OpOption{clientv3.WithRev(watchRev)} if len(args) == 2 { if watchPrefix { return nil, fmt.Errorf("`range_end` and `--prefix` are mutually exclusive") } opts = append(opts, clientv3.WithRange(args[1])) } if watchPrefix { opts = append(opts, clientv3.WithPrefix()) } if watchPrevKey { opts = append(opts, clientv3.WithPrevKV()) } return c.Watch(context.TODO(), key, opts...), nil }
func ExampleWatcher_watchWithRange() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() // watches within ['foo1', 'foo4'), in lexicographical order rch := cli.Watch(context.Background(), "foo1", clientv3.WithRange("foo4")) for wresp := range rch { for _, ev := range wresp.Events { fmt.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value) } } // PUT "foo1" : "bar" // PUT "foo2" : "bar" // PUT "foo3" : "bar" }
func newWatchBroadcast(wp *watchProxy, w *watcher, update func(*watchBroadcast)) *watchBroadcast { cctx, cancel := context.WithCancel(wp.ctx) wb := &watchBroadcast{ cancel: cancel, nextrev: w.nextrev, receivers: make(map[*watcher]struct{}), donec: make(chan struct{}), } wb.add(w) go func() { defer close(wb.donec) // loop because leader loss will close channel for cctx.Err() == nil { opts := []clientv3.OpOption{ clientv3.WithRange(w.wr.end), clientv3.WithProgressNotify(), clientv3.WithRev(wb.nextrev), clientv3.WithPrevKV(), } // The create notification should be the first response; // if the watch is recreated following leader loss, it // shouldn't post a second create response to the client. if wb.responses == 0 { opts = append(opts, clientv3.WithCreatedNotify()) } wch := wp.cw.Watch(cctx, w.wr.key, opts...) for wr := range wch { wb.bcast(wr) update(wb) } wp.retryLimiter.Wait(cctx) } }() return wb }
func TestKVRange(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) kv := clientv3.NewKV(clus.RandClient()) ctx := context.TODO() keySet := []string{"a", "b", "c", "c", "c", "foo", "foo/abc", "fop"} for i, key := range keySet { if _, err := kv.Put(ctx, key, ""); err != nil { t.Fatalf("#%d: couldn't put %q (%v)", i, key, err) } } resp, err := kv.Get(ctx, keySet[0]) if err != nil { t.Fatalf("couldn't get key (%v)", err) } wheader := resp.Header tests := []struct { begin, end string rev int64 sortOption *clientv3.SortOption serializable bool wantSet []*storagepb.KeyValue }{ // range first two { "a", "c", 0, nil, false, []*storagepb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, }, }, // range first two with serializable { "a", "c", 0, nil, true, []*storagepb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, }, }, // range all with rev { "a", "x", 2, nil, false, []*storagepb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, // range all with SortByKey, SortAscend { "a", "x", 0, &clientv3.SortOption{Target: clientv3.SortByKey, Order: clientv3.SortAscend}, false, []*storagepb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, }, }, // range all with SortByCreatedRev, SortDescend { "a", "x", 0, &clientv3.SortOption{Target: clientv3.SortByCreatedRev, Order: clientv3.SortDescend}, false, []*storagepb.KeyValue{ {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, // range all with SortByModifiedRev, SortDescend { "a", "x", 0, &clientv3.SortOption{Target: clientv3.SortByModifiedRev, Order: clientv3.SortDescend}, false, []*storagepb.KeyValue{ {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, } for i, tt := range tests { opts := []clientv3.OpOption{clientv3.WithRange(tt.end), clientv3.WithRev(tt.rev)} if tt.sortOption != nil { opts = append(opts, clientv3.WithSort(tt.sortOption.Target, tt.sortOption.Order)) } if tt.serializable == true { opts = append(opts, clientv3.WithSerializable()) } resp, err := kv.Get(ctx, tt.begin, opts...) if err != nil { t.Fatalf("#%d: couldn't range (%v)", i, err) } if !reflect.DeepEqual(wheader, resp.Header) { t.Fatalf("#%d: wheader expected %+v, got %+v", i, wheader, resp.Header) } if !reflect.DeepEqual(tt.wantSet, resp.Kvs) { t.Fatalf("#%d: resp.Kvs expected %+v, got %+v", i, tt.wantSet, resp.Kvs) } } }
func TestKVRange(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) kv := clientv3.NewKV(clus.RandClient()) ctx := context.TODO() keySet := []string{"a", "b", "c", "c", "c", "foo", "foo/abc", "fop"} for i, key := range keySet { if _, err := kv.Put(ctx, key, ""); err != nil { t.Fatalf("#%d: couldn't put %q (%v)", i, key, err) } } resp, err := kv.Get(ctx, keySet[0]) if err != nil { t.Fatalf("couldn't get key (%v)", err) } wheader := resp.Header tests := []struct { begin, end string rev int64 opts []clientv3.OpOption wantSet []*mvccpb.KeyValue }{ // range first two { "a", "c", 0, nil, []*mvccpb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, }, }, // range first two with serializable { "a", "c", 0, []clientv3.OpOption{clientv3.WithSerializable()}, []*mvccpb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, }, }, // range all with rev { "a", "x", 2, nil, []*mvccpb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, // range all with countOnly { "a", "x", 2, []clientv3.OpOption{clientv3.WithCountOnly()}, nil, }, // range all with SortByKey, SortAscend { "a", "x", 0, []clientv3.OpOption{clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)}, []*mvccpb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, }, }, // range all with SortByKey, missing sorting order (ASCEND by default) { "a", "x", 0, []clientv3.OpOption{clientv3.WithSort(clientv3.SortByKey, clientv3.SortNone)}, []*mvccpb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, }, }, // range all with SortByCreateRevision, SortDescend { "a", "x", 0, []clientv3.OpOption{clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortDescend)}, []*mvccpb.KeyValue{ {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, // range all with SortByCreateRevision, missing sorting order (ASCEND by default) { "a", "x", 0, []clientv3.OpOption{clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortNone)}, []*mvccpb.KeyValue{ {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, }, }, // range all with SortByModRevision, SortDescend { "a", "x", 0, []clientv3.OpOption{clientv3.WithSort(clientv3.SortByModRevision, clientv3.SortDescend)}, []*mvccpb.KeyValue{ {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, // WithPrefix { "foo", "", 0, []clientv3.OpOption{clientv3.WithPrefix()}, []*mvccpb.KeyValue{ {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, }, }, // WithFromKey { "fo", "", 0, []clientv3.OpOption{clientv3.WithFromKey()}, []*mvccpb.KeyValue{ {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, }, }, } for i, tt := range tests { opts := []clientv3.OpOption{clientv3.WithRange(tt.end), clientv3.WithRev(tt.rev)} opts = append(opts, tt.opts...) resp, err := kv.Get(ctx, tt.begin, opts...) if err != nil { t.Fatalf("#%d: couldn't range (%v)", i, err) } if !reflect.DeepEqual(wheader, resp.Header) { t.Fatalf("#%d: wheader expected %+v, got %+v", i, wheader, resp.Header) } if !reflect.DeepEqual(tt.wantSet, resp.Kvs) { t.Fatalf("#%d: resp.Kvs expected %+v, got %+v", i, tt.wantSet, resp.Kvs) } } }
func TestKVDeleteRange(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) kv := clientv3.NewKV(clus.RandClient()) ctx := context.TODO() tests := []struct { key string opts []clientv3.OpOption wkeys []string }{ // [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 { 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) } resp, err := kv.Get(ctx, "a", clientv3.WithFromKey()) if err != nil { t.Fatalf("#%d: couldn't get keys (%v)", i, err) } 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) } } }
func getGetOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) { if len(args) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("range command needs arguments.")) } if getPrefix && getFromKey { ExitWithError(ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one.")) } opts := []clientv3.OpOption{} switch getConsistency { case "s": opts = append(opts, clientv3.WithSerializable()) case "l": default: ExitWithError(ExitBadFeature, fmt.Errorf("unknown consistency flag %q", getConsistency)) } key := args[0] if len(args) > 1 { if getPrefix || getFromKey { ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one arguement when `--prefix` or `--from-key` is set.")) } opts = append(opts, clientv3.WithRange(args[1])) } opts = append(opts, clientv3.WithLimit(getLimit)) if getRev > 0 { opts = append(opts, clientv3.WithRev(getRev)) } sortByOrder := clientv3.SortNone sortOrder := strings.ToUpper(getSortOrder) switch { case sortOrder == "ASCEND": sortByOrder = clientv3.SortAscend case sortOrder == "DESCEND": sortByOrder = clientv3.SortDescend case sortOrder == "": // nothing default: ExitWithError(ExitBadFeature, fmt.Errorf("bad sort order %v", getSortOrder)) } sortByTarget := clientv3.SortByKey sortTarget := strings.ToUpper(getSortTarget) switch { case sortTarget == "CREATE": sortByTarget = clientv3.SortByCreateRevision case sortTarget == "KEY": sortByTarget = clientv3.SortByKey case sortTarget == "MODIFY": sortByTarget = clientv3.SortByModRevision case sortTarget == "VALUE": sortByTarget = clientv3.SortByValue case sortTarget == "VERSION": sortByTarget = clientv3.SortByVersion case sortTarget == "": // nothing default: ExitWithError(ExitBadFeature, fmt.Errorf("bad sort target %v", getSortTarget)) } opts = append(opts, clientv3.WithSort(sortByTarget, sortByOrder)) if getPrefix { opts = append(opts, clientv3.WithPrefix()) } if getFromKey { opts = append(opts, clientv3.WithFromKey()) } return key, opts }