go.etcd.io/etcd@v3.3.27+incompatible/clientv3/concurrency/session.go (about) 1 // Copyright 2016 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package concurrency 16 17 import ( 18 "context" 19 "time" 20 21 v3 "github.com/coreos/etcd/clientv3" 22 ) 23 24 const defaultSessionTTL = 60 25 26 // Session represents a lease kept alive for the lifetime of a client. 27 // Fault-tolerant applications may use sessions to reason about liveness. 28 type Session struct { 29 client *v3.Client 30 opts *sessionOptions 31 id v3.LeaseID 32 33 cancel context.CancelFunc 34 donec <-chan struct{} 35 } 36 37 // NewSession gets the leased session for a client. 38 func NewSession(client *v3.Client, opts ...SessionOption) (*Session, error) { 39 ops := &sessionOptions{ttl: defaultSessionTTL, ctx: client.Ctx()} 40 for _, opt := range opts { 41 opt(ops) 42 } 43 44 id := ops.leaseID 45 if id == v3.NoLease { 46 resp, err := client.Grant(ops.ctx, int64(ops.ttl)) 47 if err != nil { 48 return nil, err 49 } 50 id = v3.LeaseID(resp.ID) 51 } 52 53 ctx, cancel := context.WithCancel(ops.ctx) 54 keepAlive, err := client.KeepAlive(ctx, id) 55 if err != nil || keepAlive == nil { 56 cancel() 57 return nil, err 58 } 59 60 donec := make(chan struct{}) 61 s := &Session{client: client, opts: ops, id: id, cancel: cancel, donec: donec} 62 63 // keep the lease alive until client error or cancelled context 64 go func() { 65 defer close(donec) 66 for range keepAlive { 67 // eat messages until keep alive channel closes 68 } 69 }() 70 71 return s, nil 72 } 73 74 // Client is the etcd client that is attached to the session. 75 func (s *Session) Client() *v3.Client { 76 return s.client 77 } 78 79 // Lease is the lease ID for keys bound to the session. 80 func (s *Session) Lease() v3.LeaseID { return s.id } 81 82 // Done returns a channel that closes when the lease is orphaned, expires, or 83 // is otherwise no longer being refreshed. 84 func (s *Session) Done() <-chan struct{} { return s.donec } 85 86 // Orphan ends the refresh for the session lease. This is useful 87 // in case the state of the client connection is indeterminate (revoke 88 // would fail) or when transferring lease ownership. 89 func (s *Session) Orphan() { 90 s.cancel() 91 <-s.donec 92 } 93 94 // Close orphans the session and revokes the session lease. 95 func (s *Session) Close() error { 96 s.Orphan() 97 // if revoke takes longer than the ttl, lease is expired anyway 98 ctx, cancel := context.WithTimeout(s.opts.ctx, time.Duration(s.opts.ttl)*time.Second) 99 _, err := s.client.Revoke(ctx, s.id) 100 cancel() 101 return err 102 } 103 104 type sessionOptions struct { 105 ttl int 106 leaseID v3.LeaseID 107 ctx context.Context 108 } 109 110 // SessionOption configures Session. 111 type SessionOption func(*sessionOptions) 112 113 // WithTTL configures the session's TTL in seconds. 114 // If TTL is <= 0, the default 60 seconds TTL will be used. 115 func WithTTL(ttl int) SessionOption { 116 return func(so *sessionOptions) { 117 if ttl > 0 { 118 so.ttl = ttl 119 } 120 } 121 } 122 123 // WithLease specifies the existing leaseID to be used for the session. 124 // This is useful in process restart scenario, for example, to reclaim 125 // leadership from an election prior to restart. 126 func WithLease(leaseID v3.LeaseID) SessionOption { 127 return func(so *sessionOptions) { 128 so.leaseID = leaseID 129 } 130 } 131 132 // WithContext assigns a context to the session instead of defaulting to 133 // using the client context. This is useful for canceling NewSession and 134 // Close operations immediately without having to close the client. If the 135 // context is canceled before Close() completes, the session's lease will be 136 // abandoned and left to expire instead of being revoked. 137 func WithContext(ctx context.Context) SessionOption { 138 return func(so *sessionOptions) { 139 so.ctx = ctx 140 } 141 }