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) }
// TestKVGetOneEndpointDown ensures a client can connect and get if one endpoint is down func TestKVPutOneEndpointDown(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) // get endpoint list eps := make([]string, 3) for i := range eps { eps[i] = clus.Members[i].GRPCAddr() } // make a dead node clus.Members[rand.Intn(len(eps))].Stop(t) // try to connect with dead node in the endpoint list cfg := clientv3.Config{Endpoints: eps, DialTimeout: 1 * time.Second} cli, err := clientv3.New(cfg) if err != nil { t.Fatal(err) } defer cli.Close() ctx, cancel := context.WithTimeout(context.TODO(), 3*time.Second) if _, err := cli.Get(ctx, "abc", clientv3.WithSerializable()); err != nil { t.Fatal(err) } cancel() }
// TestDialSetEndpoints ensures SetEndpoints can replace unavailable endpoints with available ones. func TestDialSetEndpoints(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) // get endpoint list eps := make([]string, 3) for i := range eps { eps[i] = clus.Members[i].GRPCAddr() } toKill := rand.Intn(len(eps)) cfg := clientv3.Config{Endpoints: []string{eps[toKill]}, DialTimeout: 1 * time.Second} cli, err := clientv3.New(cfg) if err != nil { t.Fatal(err) } defer cli.Close() // make a dead node clus.Members[toKill].Stop(t) clus.WaitLeader(t) // update client with available endpoints cli.SetEndpoints(eps[(toKill+1)%3]) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) if _, err = cli.Get(ctx, "foo", clientv3.WithSerializable()); err != nil { t.Fatal(err) } cancel() }
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 watchGetFunc(cmd *cobra.Command, args []string) { clients := mustCreateClients(totalClients, totalConns) getClient := mustCreateClients(1, 1) // setup keys for watchers watchRev := int64(0) for i := 0; i < watchEvents; i++ { v := fmt.Sprintf("%d", i) resp, err := clients[0].Put(context.TODO(), "watchkey", v) if err != nil { panic(err) } if i == 0 { watchRev = resp.Header.Revision } } streams := make([]v3.Watcher, watchGetTotalStreams) for i := range streams { streams[i] = v3.NewWatcher(clients[i%len(clients)]) } bar = pb.New(watchGetTotalWatchers * watchEvents) bar.Format("Bom !") bar.Start() // report from trying to do serialized gets with concurrent watchers r := newReport() ctx, cancel := context.WithCancel(context.TODO()) f := func() { defer close(r.Results()) for { st := time.Now() _, err := getClient[0].Get(ctx, "abc", v3.WithSerializable()) if ctx.Err() != nil { break } r.Results() <- report.Result{Err: err, Start: st, End: time.Now()} } } wg.Add(watchGetTotalWatchers) for i := 0; i < watchGetTotalWatchers; i++ { go doUnsyncWatch(streams[i%len(streams)], watchRev, f) } rc := r.Run() wg.Wait() cancel() bar.Finish() fmt.Printf("Get during watch summary:\n%s", <-rc) }
func newStore(c *clientv3.Client, quorumRead bool, codec runtime.Codec, prefix string) *store { versioner := etcd.APIObjectVersioner{} result := &store{ client: c, versioner: versioner, codec: codec, pathPrefix: prefix, watcher: newWatcher(c, codec, versioner), } if !quorumRead { // In case of non-quorum reads, we can set WithSerializable() // options for all Get operations. result.getOps = append(result.getOps, clientv3.WithSerializable()) } return result }
func doSerializedGet(ctx context.Context, client *v3.Client, results chan result) { for { st := time.Now() _, err := client.Get(ctx, "abc", v3.WithSerializable()) if ctx.Err() != nil { break } var errStr string if err != nil { errStr = err.Error() } res := result{errStr: errStr, duration: time.Since(st), happened: time.Now()} results <- res } close(results) }
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 (s *stmSerializable) Get(key string) string { if wv, ok := s.wset[key]; ok { return wv.val } firstRead := len(s.rset) == 0 if resp, ok := s.prefetch[key]; ok { delete(s.prefetch, key) s.rset[key] = resp } resp := s.stm.fetch(key) if firstRead { // txn's base revision is defined by the first read s.getOpts = []v3.OpOption{ v3.WithRev(resp.Header.Revision), v3.WithSerializable(), } } return respToValue(resp) }
func (gw *gRPCWatcher) firstNext() ([]*naming.Update, error) { // Use serialized request so resolution still works if the target etcd // server is partitioned away from the quorum. resp, err := gw.c.Get(gw.ctx, gw.target, etcd.WithPrefix(), etcd.WithSerializable()) if gw.err = err; err != nil { return nil, err } updates := make([]*naming.Update, 0, len(resp.Kvs)) for _, kv := range resp.Kvs { var jupdate naming.Update if err := json.Unmarshal(kv.Value, &jupdate); err != nil { continue } updates = append(updates, &jupdate) } opts := []etcd.OpOption{etcd.WithRev(resp.Header.Revision + 1), etcd.WithPrefix(), etcd.WithPrevKV()} gw.wch = gw.c.Watch(gw.ctx, gw.target, opts...) return updates, nil }
// TestKVGetResetLoneEndpoint ensures that if an endpoint resets and all other // endpoints are down, then it will reconnect. func TestKVGetResetLoneEndpoint(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2}) defer clus.Terminate(t) // get endpoint list eps := make([]string, 2) for i := range eps { eps[i] = clus.Members[i].GRPCAddr() } cfg := clientv3.Config{Endpoints: eps, DialTimeout: 500 * time.Millisecond} cli, err := clientv3.New(cfg) if err != nil { t.Fatal(err) } defer cli.Close() // disconnect everything clus.Members[0].Stop(t) clus.Members[1].Stop(t) // have Get try to reconnect donec := make(chan struct{}) go func() { ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) if _, err := cli.Get(ctx, "abc", clientv3.WithSerializable()); err != nil { t.Fatal(err) } cancel() close(donec) }() time.Sleep(500 * time.Millisecond) clus.Members[0].Restart(t) select { case <-time.After(10 * time.Second): t.Fatalf("timed out waiting for Get") case <-donec: } }
func prepareObjs(ctx context.Context, e *event, client *clientv3.Client, codec runtime.Codec, versioner storage.Versioner) (curObj runtime.Object, oldObj runtime.Object, err error) { if !e.isDeleted { curObj, err = decodeObj(codec, versioner, e.value, e.rev) if err != nil { return nil, nil, err } } if e.isDeleted || !e.isCreated { getResp, err := client.Get(ctx, e.key, clientv3.WithRev(e.rev-1), clientv3.WithSerializable()) if err != nil { return nil, nil, err } // Note that this sends the *old* object with the etcd revision for the time at // which it gets deleted. // We assume old object is returned only in Deleted event. Users (e.g. cacher) need // to have larger than previous rev to tell the ordering. oldObj, err = decodeObj(codec, versioner, getResp.Kvs[0].Value, e.rev) if err != nil { return nil, nil, err } } return curObj, oldObj, nil }
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 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 }
// NewSTMRepeatable initiates new repeatable read transaction; reads within // the same transaction attempt always return the same data. func NewSTMRepeatable(ctx context.Context, c *v3.Client, apply func(STM) error) (*v3.TxnResponse, error) { s := &stm{client: c, ctx: ctx, getOpts: []v3.OpOption{v3.WithSerializable()}} return runSTM(s, apply) }
// sync tries to retrieve existing data and send them to process. // The revision to watch will be set to the revision in response. func (wc *watchChan) sync() error { opts := []clientv3.OpOption{} if wc.recursive { opts = append(opts, clientv3.WithPrefix()) } getResp, err := wc.watcher.client.Get(wc.ctx, wc.key, opts...) if err != nil { return err } wc.initialRev = getResp.Header.Revision for _, kv := range getResp.Kvs { prevResp, err := wc.watcher.client.Get(wc.ctx, string(kv.Key), clientv3.WithRev(kv.ModRevision-1), clientv3.WithSerializable()) if err != nil { return err } var prevVal []byte if len(prevResp.Kvs) > 0 { prevVal = prevResp.Kvs[0].Value } wc.sendEvent(parseKV(kv, prevVal)) } return nil }