func ExampleLease_create() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() kvc := clientv3.NewKV(cli) lapi := clientv3.NewLease(cli) defer lapi.Close() // minimum lease TTL is 5-second resp, err := lapi.Create(context.TODO(), 5) if err != nil { log.Fatal(err) } // after 5 seconds, the key 'foo' will be removed _, err = kvc.Put(context.TODO(), "foo", "bar", clientv3.WithLease(lease.LeaseID(resp.ID))) if err != nil { log.Fatal(err) } }
func ExampleLease_keepAliveOnce() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() resp, err := cli.Grant(context.TODO(), 5) if err != nil { log.Fatal(err) } _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(clientv3.LeaseID(resp.ID))) if err != nil { log.Fatal(err) } // to renew the lease only once _, err = cli.KeepAliveOnce(context.TODO(), clientv3.LeaseID(resp.ID)) if err != nil { log.Fatal(err) } }
func ExampleLease_keepAliveOnce() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() resp, err := cli.Grant(context.TODO(), 5) if err != nil { log.Fatal(err) } _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID)) if err != nil { log.Fatal(err) } // to renew the lease only once ka, kaerr := cli.KeepAliveOnce(context.TODO(), resp.ID) if kaerr != nil { log.Fatal(kaerr) } fmt.Println("ttl:", ka.TTL) // Output: ttl: 5 }
func getPutOp(cmd *cobra.Command, args []string) (string, string, []clientv3.OpOption) { if len(args) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("put command needs 1 argument and input from stdin or 2 arguments.")) } key := args[0] value, err := argOrStdin(args, os.Stdin, 1) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("put command needs 1 argument and input from stdin or 2 arguments.")) } id, err := strconv.ParseInt(leaseStr, 16, 64) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID (%v), expecting ID in Hex", err)) } opts := []clientv3.OpOption{} if id != 0 { opts = append(opts, clientv3.WithLease(clientv3.LeaseID(id))) } if putPrevKV { opts = append(opts, clientv3.WithPrevKV()) } return key, value, opts }
func ExampleLease_revoke() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() resp, err := cli.Create(context.TODO(), 5) if err != nil { log.Fatal(err) } _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(clientv3.LeaseID(resp.ID))) if err != nil { log.Fatal(err) } // revoking lease expires the key attached to its lease ID _, err = cli.Revoke(context.TODO(), clientv3.LeaseID(resp.ID)) if err != nil { log.Fatal(err) } gresp, err := cli.Get(context.TODO(), "foo") if err != nil { log.Fatal(err) } fmt.Println("number of keys:", len(gresp.Kvs)) // number of keys: 0 }
func ExampleLease_keepAlive() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() resp, err := cli.Create(context.TODO(), 5) if err != nil { log.Fatal(err) } _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(clientv3.LeaseID(resp.ID))) if err != nil { log.Fatal(err) } // the key 'foo' will be kept forever _, err = cli.KeepAlive(context.TODO(), clientv3.LeaseID(resp.ID)) if err != nil { log.Fatal(err) } }
func TestLeaseRevoke(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) lapi := clientv3.NewLease(clus.RandClient()) defer lapi.Close() kv := clientv3.NewKV(clus.RandClient()) resp, err := lapi.Create(context.Background(), 10) if err != nil { t.Errorf("failed to create lease %v", err) } _, err = lapi.Revoke(context.Background(), lease.LeaseID(resp.ID)) if err != nil { t.Errorf("failed to revoke lease %v", err) } _, err = kv.Put(context.TODO(), "foo", "bar", clientv3.WithLease(lease.LeaseID(resp.ID))) if err != v3rpc.ErrLeaseNotFound { t.Fatalf("err = %v, want %v", err, v3rpc.ErrLeaseNotFound) } }
func ExampleLease_keepAlive() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() resp, err := cli.Grant(context.TODO(), 5) if err != nil { log.Fatal(err) } _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID)) if err != nil { log.Fatal(err) } // the key 'foo' will be kept forever ch, kaerr := cli.KeepAlive(context.TODO(), resp.ID) if kaerr != nil { log.Fatal(kaerr) } ka := <-ch fmt.Println("ttl:", ka.TTL) // Output: ttl: 5 }
// Lock locks the mutex with a cancellable context. If the context is cancelled // while trying to acquire the lock, the mutex tries to clean its stale lock entry. func (m *Mutex) Lock(ctx context.Context) error { s, serr := NewSession(m.client) if serr != nil { return serr } m.myKey = fmt.Sprintf("%s/%x", m.pfx, s.Lease()) cmp := v3.Compare(v3.CreateRevision(m.myKey), "=", 0) // put self in lock waiters via myKey; oldest waiter holds lock put := v3.OpPut(m.myKey, "", v3.WithLease(s.Lease())) // reuse key in case this session already holds the lock get := v3.OpGet(m.myKey) resp, err := m.client.Txn(ctx).If(cmp).Then(put).Else(get).Commit() if err != nil { return err } m.myRev = resp.Header.Revision if !resp.Succeeded { m.myRev = resp.Responses[0].GetResponseRange().Kvs[0].CreateRevision } // wait for deletion revisions prior to myKey err = waitDeletes(ctx, m.client, m.pfx, v3.WithPrefix(), v3.WithRev(m.myRev-1)) // release lock key if cancelled select { case <-ctx.Done(): m.Unlock() default: } return err }
// ttlOpts returns client options based on given ttl. // ttl: if ttl is non-zero, it will attach the key to a lease with ttl of roughly the same length func (s *store) ttlOpts(ctx context.Context, ttl int64) ([]clientv3.OpOption, error) { if ttl == 0 { return nil, nil } // TODO: one lease per ttl key is expensive. Based on current use case, we can have a long window to // put keys within into same lease. We shall benchmark this and optimize the performance. lcr, err := s.client.Lease.Grant(ctx, ttl) if err != nil { return nil, err } return []clientv3.OpOption{clientv3.WithLease(clientv3.LeaseID(lcr.ID))}, nil }
// putNewKV attempts to create the given key, only succeeding if the key did // not yet exist. func putNewKV(kv v3.KV, key, val string, leaseID v3.LeaseID) (int64, error) { cmp := v3.Compare(v3.Version(key), "=", 0) req := v3.OpPut(key, val, v3.WithLease(leaseID)) txnresp, err := kv.Txn(context.TODO()).If(cmp).Then(req).Commit() if err != nil { return 0, err } if !txnresp.Succeeded { return 0, ErrKeyExists } return txnresp.Header.Revision, nil }
// newSequentialKV allocates a new sequential key <prefix>/nnnnn with a given // value and lease. Note: a bookkeeping node __<prefix> is also allocated. func newSequentialKV(kv v3.KV, prefix, val string, leaseID v3.LeaseID) (*RemoteKV, error) { resp, err := kv.Get(context.TODO(), prefix, v3.WithLastKey()...) if err != nil { return nil, err } // add 1 to last key, if any newSeqNum := 0 if len(resp.Kvs) != 0 { fields := strings.Split(string(resp.Kvs[0].Key), "/") _, serr := fmt.Sscanf(fields[len(fields)-1], "%d", &newSeqNum) if serr != nil { return nil, serr } newSeqNum++ } newKey := fmt.Sprintf("%s/%016d", prefix, newSeqNum) // base prefix key must be current (i.e., <=) with the server update; // the base key is important to avoid the following: // N1: LastKey() == 1, start txn. // N2: New Key 2, New Key 3, Delete Key 2 // N1: txn succeeds allocating key 2 when it shouldn't baseKey := "__" + prefix // current revision might contain modification so +1 cmp := v3.Compare(v3.ModRevision(baseKey), "<", resp.Header.Revision+1) reqPrefix := v3.OpPut(baseKey, "", v3.WithLease(leaseID)) reqNewKey := v3.OpPut(newKey, val, v3.WithLease(leaseID)) txn := kv.Txn(context.TODO()) txnresp, err := txn.If(cmp).Then(reqPrefix, reqNewKey).Commit() if err != nil { return nil, err } if !txnresp.Succeeded { return newSequentialKV(kv, prefix, val, leaseID) } return &RemoteKV{kv, newKey, txnresp.Header.Revision, val}, nil }
func TestLeaseTimeToLive(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) lapi := clientv3.NewLease(clus.RandClient()) defer lapi.Close() resp, err := lapi.Grant(context.Background(), 10) if err != nil { t.Errorf("failed to create lease %v", err) } kv := clientv3.NewKV(clus.RandClient()) keys := []string{"foo1", "foo2"} for i := range keys { if _, err = kv.Put(context.TODO(), keys[i], "bar", clientv3.WithLease(resp.ID)); err != nil { t.Fatal(err) } } lresp, lerr := lapi.TimeToLive(context.Background(), resp.ID, clientv3.WithAttachedKeys()) if lerr != nil { t.Fatal(lerr) } if lresp.ID != resp.ID { t.Fatalf("leaseID expected %d, got %d", resp.ID, lresp.ID) } if lresp.GrantedTTL != int64(10) { t.Fatalf("GrantedTTL expected %d, got %d", 10, lresp.GrantedTTL) } if lresp.TTL == 0 || lresp.TTL > lresp.GrantedTTL { t.Fatalf("unexpected TTL %d (granted %d)", lresp.TTL, lresp.GrantedTTL) } ks := make([]string, len(lresp.Keys)) for i := range lresp.Keys { ks[i] = string(lresp.Keys[i]) } sort.Strings(ks) if !reflect.DeepEqual(ks, keys) { t.Fatalf("keys expected %v, got %v", keys, ks) } lresp, lerr = lapi.TimeToLive(context.Background(), resp.ID) if lerr != nil { t.Fatal(lerr) } if len(lresp.Keys) != 0 { t.Fatalf("unexpected keys %+v", lresp.Keys) } }
func (n *ng) setVal(key string, val []byte, ttl time.Duration) error { ops := []etcd.OpOption{} if ttl > 0 { lgr, err := n.client.Grant(n.context, int64(ttl.Seconds())) if err != nil { return err } ops = append(ops, etcd.WithLease(lgr.ID)) } _, err := n.client.Put(n.context, key, string(val), ops...) return convertErr(err) }
// Lock locks resources on sync // This call blocks until you can get lock func (s *Sync) Lock(path string, block bool) error { for { if s.HasLock(path) { return nil } var err error lease, err := s.etcdClient.Grant(s.withTimeout(), masterTTL) var resp *etcd.TxnResponse if err == nil { cmp := etcd.Compare(etcd.CreateRevision(path), "=", 0) put := etcd.OpPut(path, s.processID, etcd.WithLease(lease.ID)) resp, err = s.etcdClient.Txn(s.withTimeout()).If(cmp).Then(put).Commit() } if err != nil || !resp.Succeeded { msg := fmt.Sprintf("failed to lock path %s", path) if err != nil { msg = fmt.Sprintf("failed to lock path %s: %s", path, err) } log.Notice(msg) s.locks.Remove(path) if !block { return errors.New(msg) } time.Sleep(masterTTL * time.Second) continue } log.Info("Locked %s", path) s.locks.Set(path, lease.ID) //Refresh master token go func() { defer func() { log.Notice("releasing keepalive lock for %s", path) s.locks.Remove(path) }() for s.HasLock(path) { ch, err := s.etcdClient.KeepAlive(s.withTimeout(), lease.ID) if err != nil { log.Notice("failed to keepalive lock for %s %s", path, err) return } for range ch { } } }() return nil } }
func (c *etcdCtx) RegisterPeer(lease int64, p *models.PeerInfo) error { if lease == 0 { return errors.New("no lease") } promOps.WithLabelValues("register-peer").Inc() p.LastSeen = time.Now().UnixNano() data, err := p.Marshal() if err != nil { return err } lid := etcdv3.LeaseID(lease) _, err = c.etcd.Client.Put( c.getContext(), MkKey("nodes", p.UUID), string(data), etcdv3.WithLease(lid)) return err }
func TestLeaseNotFoundError(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) defer clus.Terminate(t) lapi := clientv3.NewLease(clus.RandClient()) defer lapi.Close() kv := clientv3.NewKV(clus.RandClient()) _, err := kv.Put(context.TODO(), "foo", "bar", clientv3.WithLease(clientv3.LeaseID(500))) if err != rpctypes.ErrLeaseNotFound { t.Fatalf("expected %v, got %v", rpctypes.ErrLeaseNotFound, err) } }
// Lock locks the mutex with a cancellable context. If the context is cancelled // while trying to acquire the lock, the mutex tries to clean its stale lock entry. func (m *Mutex) Lock(ctx context.Context) error { s, err := NewSession(m.client) if err != nil { return err } // put self in lock waiters via myKey; oldest waiter holds lock m.myKey, m.myRev, err = NewUniqueKey(ctx, m.client, m.pfx, v3.WithLease(s.Lease())) // wait for deletion revisions prior to myKey err = waitDeletes(ctx, m.client, m.pfx, v3.WithPrefix(), v3.WithRev(m.myRev-1)) // release lock key if cancelled select { case <-ctx.Done(): m.Unlock() default: } return err }
// Proclaim lets the leader announce a new value without another election. func (e *Election) Proclaim(ctx context.Context, val string) error { if e.leaderSession == nil { return ErrElectionNotLeader } cmp := v3.Compare(v3.CreateRevision(e.leaderKey), "=", e.leaderRev) txn := e.client.Txn(ctx).If(cmp) txn = txn.Then(v3.OpPut(e.leaderKey, val, v3.WithLease(e.leaderSession.Lease()))) tresp, terr := txn.Commit() if terr != nil { return terr } if !tresp.Succeeded { e.leaderKey = "" return ErrElectionNotLeader } return nil }
func TestKVPut(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) lapi := clientv3.NewLease(clus.RandClient()) defer lapi.Close() kv := clientv3.NewKV(clus.RandClient()) ctx := context.TODO() resp, err := lapi.Create(context.Background(), 10) if err != nil { t.Fatalf("failed to create lease %v", err) } tests := []struct { key, val string leaseID lease.LeaseID }{ {"foo", "bar", lease.NoLease}, {"hello", "world", lease.LeaseID(resp.ID)}, } for i, tt := range tests { if _, err := kv.Put(ctx, tt.key, tt.val, clientv3.WithLease(tt.leaseID)); err != nil { t.Fatalf("#%d: couldn't put %q (%v)", i, tt.key, err) } resp, err := kv.Get(ctx, tt.key) if err != nil { t.Fatalf("#%d: couldn't get key (%v)", i, err) } if len(resp.Kvs) != 1 { t.Fatalf("#%d: expected 1 key, got %d", i, len(resp.Kvs)) } if !bytes.Equal([]byte(tt.val), resp.Kvs[0].Value) { t.Errorf("#%d: val = %s, want %s", i, tt.val, resp.Kvs[0].Value) } if tt.leaseID != lease.LeaseID(resp.Kvs[0].Lease) { t.Errorf("#%d: val = %d, want %d", i, tt.leaseID, resp.Kvs[0].Lease) } } }
func (b *blockEtcd) Lock(lease int64) error { if lease == 0 { return torus.ErrInvalid } k := etcd.MkKey("volumemeta", etcd.Uint64ToHex(uint64(b.vid)), "blocklock") tx := b.Etcd.Client.Txn(b.getContext()).If( etcdv3.Compare(etcdv3.Version(k), "=", 0), ).Then( etcdv3.OpPut(k, b.Etcd.UUID(), etcdv3.WithLease(etcdv3.LeaseID(lease))), ) resp, err := tx.Commit() if err != nil { return err } if !resp.Succeeded { return torus.ErrLocked } return nil }
// Campaign puts a value as eligible for the election. It blocks until // it is elected, an error occurs, or the context is cancelled. func (e *Election) Campaign(ctx context.Context, val string) error { s, serr := NewSession(e.client) if serr != nil { return serr } k := fmt.Sprintf("%s/%x", e.keyPrefix, s.Lease()) txn := e.client.Txn(ctx).If(v3.Compare(v3.CreateRevision(k), "=", 0)) txn = txn.Then(v3.OpPut(k, val, v3.WithLease(s.Lease()))) txn = txn.Else(v3.OpGet(k)) resp, err := txn.Commit() if err != nil { return err } e.leaderKey, e.leaderRev, e.leaderSession = k, resp.Header.Revision, s if !resp.Succeeded { kv := resp.Responses[0].GetResponseRange().Kvs[0] e.leaderRev = kv.CreateRevision if string(kv.Value) != val { if err = e.Proclaim(ctx, val); err != nil { e.Resign(ctx) return err } } } err = waitDeletes(ctx, e.client, e.keyPrefix, v3.WithPrefix(), v3.WithRev(e.leaderRev-1)) if err != nil { // clean up in case of context cancel select { case <-ctx.Done(): e.Resign(e.client.Ctx()) default: e.leaderSession = nil } return err } return nil }
func TestLeaseGrant(t *testing.T) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) lapi := clientv3.NewLease(clus.RandClient()) defer lapi.Close() kv := clientv3.NewKV(clus.RandClient()) resp, err := lapi.Grant(context.Background(), 10) if err != nil { t.Errorf("failed to create lease %v", err) } _, err = kv.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID)) if err != nil { t.Fatalf("failed to create key with lease %v", err) } }
// Lock locks the mutex with a cancellable context. If the context is cancelled // while trying to acquire the lock, the mutex tries to clean its stale lock entry. func (m *Mutex) Lock(ctx context.Context) error { s, err := NewSession(m.client) if err != nil { return err } // put self in lock waiters via myKey; oldest waiter holds lock m.myKey, m.myRev, err = NewUniqueKey(ctx, m.kv, m.pfx, v3.WithLease(s.Lease())) // wait for lock to become available for err == nil { // find oldest element in waiters via revision of insertion var resp *v3.GetResponse resp, err = m.kv.Get(ctx, m.pfx, v3.WithFirstRev()...) if err != nil { break } if m.myRev == resp.Kvs[0].CreateRevision { // myKey is oldest in waiters; myKey holds the lock now return nil } // otherwise myKey isn't lowest, so there must be a pfx prior to myKey opts := append(v3.WithLastRev(), v3.WithRev(m.myRev-1)) resp, err = m.kv.Get(ctx, m.pfx, opts...) if err != nil { break } lastKey := string(resp.Kvs[0].Key) // wait for release on prior pfx err = waitUpdate(ctx, m.client, lastKey, v3.WithRev(m.myRev)) // try again in case lastKey left the wait list before acquiring the lock; // myKey can only hold the lock if it's the oldest in the list } // release lock key if cancelled select { case <-ctx.Done(): m.Unlock() default: } return err }
func ExampleLease_grant() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() // minimum lease TTL is 5-second resp, err := cli.Grant(context.TODO(), 5) if err != nil { log.Fatal(err) } // after 5 seconds, the key 'foo' will be removed _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID)) if err != nil { log.Fatal(err) } }
func main() { flag.Parse() if *etcdAddress == "" { glog.Fatalf("--etcd-address flag is required") } client, err := clientv3.New(clientv3.Config{Endpoints: []string{*etcdAddress}}) if err != nil { glog.Fatalf("Error while creating etcd client: %v", err) } // Make sure that ttlKeysPrefix is ended with "/" so that we only get children "directories". if !strings.HasSuffix(*ttlKeysPrefix, "/") { *ttlKeysPrefix += "/" } ctx := context.Background() objectsResp, err := client.KV.Get(ctx, *ttlKeysPrefix, clientv3.WithPrefix()) if err != nil { glog.Fatalf("Error while getting objects to attach to the lease") } lease, err := client.Lease.Grant(ctx, int64(*leaseDuration/time.Second)) if err != nil { glog.Fatalf("Error while creating lease: %v", err) } glog.Infof("Lease with TTL: %v created", lease.TTL) glog.Infof("Attaching lease to %d entries", len(objectsResp.Kvs)) for _, kv := range objectsResp.Kvs { _, err := client.KV.Put(ctx, string(kv.Key), string(kv.Value), clientv3.WithLease(lease.ID)) if err != nil { glog.Errorf("Error while attaching lease to: %s", string(kv.Key)) } } }
// Campaign puts a value as eligible for the election. It blocks until // it is elected, an error occurs, or the context is cancelled. func (e *Election) Campaign(ctx context.Context, val string) error { s, serr := NewSession(e.client) if serr != nil { return serr } k, rev, err := NewUniqueKV(ctx, e.client, e.keyPrefix, val, v3.WithLease(s.Lease())) if err == nil { err = waitDeletes(ctx, e.client, e.keyPrefix, v3.WithPrefix(), v3.WithRev(rev-1)) } if err != nil { // clean up in case of context cancel select { case <-ctx.Done(): e.client.Delete(e.client.Ctx(), k) default: } return err } e.leaderKey, e.leaderRev, e.leaderSession = k, rev, s return nil }
func (e *etcdv3Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error { if len(s.Nodes) == 0 { return errors.New("Require at least one node") } //refreshing lease if existing leaseID, ok := e.leases[s.Name] if ok { _, err := e.client.KeepAliveOnce(context.TODO(), leaseID) if err != nil { return err } } var options registry.RegisterOptions for _, o := range opts { o(&options) } // create hash of service; uint64 h, err := hash.Hash(s, nil) if err != nil { return err } // get existing hash e.Lock() v, ok := e.register[s.Name] e.Unlock() // the service is unchanged, skip registering if ok && v == h { return nil } service := ®istry.Service{ Name: s.Name, Version: s.Version, Metadata: s.Metadata, Endpoints: s.Endpoints, } ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout) defer cancel() var lgr *clientv3.LeaseGrantResponse if options.TTL.Seconds() > 0 { lgr, err = e.client.Grant(ctx, int64(options.TTL.Seconds())) if err != nil { return err } } for _, node := range s.Nodes { service.Nodes = []*registry.Node{node} if lgr != nil { _, err = e.client.Put(ctx, nodePath(service.Name, node.Id), encode(service), clientv3.WithLease(lgr.ID)) } else { _, err = e.client.Put(ctx, nodePath(service.Name, node.Id), encode(service)) } if err != nil { return err } } e.Lock() // save our hash of the service e.register[s.Name] = h // save our leaseID of the service if lgr != nil { e.leases[s.Name] = lgr.ID } e.Unlock() return nil }
func PutRequestToOp(r *pb.PutRequest) clientv3.Op { opts := []clientv3.OpOption{} opts = append(opts, clientv3.WithLease(clientv3.LeaseID(r.Lease))) return clientv3.OpPut(string(r.Key), string(r.Value), opts...) }
func TestWatchEventType(t *testing.T) { cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) defer cluster.Terminate(t) client := cluster.RandClient() ctx := context.Background() watchChan := client.Watch(ctx, "/", clientv3.WithPrefix()) if _, err := client.Put(ctx, "/toDelete", "foo"); err != nil { t.Fatalf("Put failed: %v", err) } if _, err := client.Put(ctx, "/toDelete", "bar"); err != nil { t.Fatalf("Put failed: %v", err) } if _, err := client.Delete(ctx, "/toDelete"); err != nil { t.Fatalf("Delete failed: %v", err) } lcr, err := client.Lease.Grant(ctx, 1) if err != nil { t.Fatalf("lease create failed: %v", err) } if _, err := client.Put(ctx, "/toExpire", "foo", clientv3.WithLease(lcr.ID)); err != nil { t.Fatalf("Put failed: %v", err) } tests := []struct { et mvccpb.Event_EventType isCreate bool isModify bool }{{ et: clientv3.EventTypePut, isCreate: true, }, { et: clientv3.EventTypePut, isModify: true, }, { et: clientv3.EventTypeDelete, }, { et: clientv3.EventTypePut, isCreate: true, }, { et: clientv3.EventTypeDelete, }} var res []*clientv3.Event for { select { case wres := <-watchChan: res = append(res, wres.Events...) case <-time.After(10 * time.Second): t.Fatalf("Should receive %d events and then break out loop", len(tests)) } if len(res) == len(tests) { break } } for i, tt := range tests { ev := res[i] if tt.et != ev.Type { t.Errorf("#%d: event type want=%s, get=%s", i, tt.et, ev.Type) } if tt.isCreate && !ev.IsCreate() { t.Errorf("#%d: event should be CreateEvent", i) } if tt.isModify && !ev.IsModify() { t.Errorf("#%d: event should be ModifyEvent", i) } } }