go.etcd.io/etcd@v3.3.27+incompatible/clientv3/leasing/cache.go (about)

     1  // Copyright 2017 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 leasing
    16  
    17  import (
    18  	"context"
    19  	"strings"
    20  	"sync"
    21  	"time"
    22  
    23  	v3 "github.com/coreos/etcd/clientv3"
    24  	v3pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
    25  	"github.com/coreos/etcd/mvcc/mvccpb"
    26  )
    27  
    28  const revokeBackoff = 2 * time.Second
    29  
    30  type leaseCache struct {
    31  	mu      sync.RWMutex
    32  	entries map[string]*leaseKey
    33  	revokes map[string]time.Time
    34  	header  *v3pb.ResponseHeader
    35  }
    36  
    37  type leaseKey struct {
    38  	response *v3.GetResponse
    39  	// rev is the leasing key revision.
    40  	rev   int64
    41  	waitc chan struct{}
    42  }
    43  
    44  func (lc *leaseCache) Rev(key string) int64 {
    45  	lc.mu.RLock()
    46  	defer lc.mu.RUnlock()
    47  	if li := lc.entries[key]; li != nil {
    48  		return li.rev
    49  	}
    50  	return 0
    51  }
    52  
    53  func (lc *leaseCache) Lock(key string) (chan<- struct{}, int64) {
    54  	lc.mu.Lock()
    55  	defer lc.mu.Unlock()
    56  	if li := lc.entries[key]; li != nil {
    57  		li.waitc = make(chan struct{})
    58  		return li.waitc, li.rev
    59  	}
    60  	return nil, 0
    61  }
    62  
    63  func (lc *leaseCache) LockRange(begin, end string) (ret []chan<- struct{}) {
    64  	lc.mu.Lock()
    65  	defer lc.mu.Unlock()
    66  	for k, li := range lc.entries {
    67  		if inRange(k, begin, end) {
    68  			li.waitc = make(chan struct{})
    69  			ret = append(ret, li.waitc)
    70  		}
    71  	}
    72  	return ret
    73  }
    74  
    75  func inRange(k, begin, end string) bool {
    76  	if strings.Compare(k, begin) < 0 {
    77  		return false
    78  	}
    79  	if end != "\x00" && strings.Compare(k, end) >= 0 {
    80  		return false
    81  	}
    82  	return true
    83  }
    84  
    85  func (lc *leaseCache) LockWriteOps(ops []v3.Op) (ret []chan<- struct{}) {
    86  	for _, op := range ops {
    87  		if op.IsGet() {
    88  			continue
    89  		}
    90  		key := string(op.KeyBytes())
    91  		if end := string(op.RangeBytes()); end == "" {
    92  			if wc, _ := lc.Lock(key); wc != nil {
    93  				ret = append(ret, wc)
    94  			}
    95  		} else {
    96  			for k := range lc.entries {
    97  				if !inRange(k, key, end) {
    98  					continue
    99  				}
   100  				if wc, _ := lc.Lock(k); wc != nil {
   101  					ret = append(ret, wc)
   102  				}
   103  			}
   104  		}
   105  	}
   106  	return ret
   107  }
   108  
   109  func (lc *leaseCache) NotifyOps(ops []v3.Op) (wcs []<-chan struct{}) {
   110  	for _, op := range ops {
   111  		if op.IsGet() {
   112  			if _, wc := lc.notify(string(op.KeyBytes())); wc != nil {
   113  				wcs = append(wcs, wc)
   114  			}
   115  		}
   116  	}
   117  	return wcs
   118  }
   119  
   120  func (lc *leaseCache) MayAcquire(key string) bool {
   121  	lc.mu.RLock()
   122  	lr, ok := lc.revokes[key]
   123  	lc.mu.RUnlock()
   124  	return !ok || time.Since(lr) > revokeBackoff
   125  }
   126  
   127  func (lc *leaseCache) Add(key string, resp *v3.GetResponse, op v3.Op) *v3.GetResponse {
   128  	lk := &leaseKey{resp, resp.Header.Revision, closedCh}
   129  	lc.mu.Lock()
   130  	if lc.header == nil || lc.header.Revision < resp.Header.Revision {
   131  		lc.header = resp.Header
   132  	}
   133  	lc.entries[key] = lk
   134  	ret := lk.get(op)
   135  	lc.mu.Unlock()
   136  	return ret
   137  }
   138  
   139  func (lc *leaseCache) Update(key, val []byte, respHeader *v3pb.ResponseHeader) {
   140  	li := lc.entries[string(key)]
   141  	if li == nil {
   142  		return
   143  	}
   144  	cacheResp := li.response
   145  	if len(cacheResp.Kvs) == 0 {
   146  		kv := &mvccpb.KeyValue{
   147  			Key:            key,
   148  			CreateRevision: respHeader.Revision,
   149  		}
   150  		cacheResp.Kvs = append(cacheResp.Kvs, kv)
   151  		cacheResp.Count = 1
   152  	}
   153  	cacheResp.Kvs[0].Version++
   154  	if cacheResp.Kvs[0].ModRevision < respHeader.Revision {
   155  		cacheResp.Header = respHeader
   156  		cacheResp.Kvs[0].ModRevision = respHeader.Revision
   157  		cacheResp.Kvs[0].Value = val
   158  	}
   159  }
   160  
   161  func (lc *leaseCache) Delete(key string, hdr *v3pb.ResponseHeader) {
   162  	lc.mu.Lock()
   163  	defer lc.mu.Unlock()
   164  	lc.delete(key, hdr)
   165  }
   166  
   167  func (lc *leaseCache) delete(key string, hdr *v3pb.ResponseHeader) {
   168  	if li := lc.entries[key]; li != nil && hdr.Revision >= li.response.Header.Revision {
   169  		li.response.Kvs = nil
   170  		li.response.Header = copyHeader(hdr)
   171  	}
   172  }
   173  
   174  func (lc *leaseCache) Evict(key string) (rev int64) {
   175  	lc.mu.Lock()
   176  	defer lc.mu.Unlock()
   177  	if li := lc.entries[key]; li != nil {
   178  		rev = li.rev
   179  		delete(lc.entries, key)
   180  		lc.revokes[key] = time.Now()
   181  	}
   182  	return rev
   183  }
   184  
   185  func (lc *leaseCache) EvictRange(key, end string) {
   186  	lc.mu.Lock()
   187  	defer lc.mu.Unlock()
   188  	for k := range lc.entries {
   189  		if inRange(k, key, end) {
   190  			delete(lc.entries, key)
   191  			lc.revokes[key] = time.Now()
   192  		}
   193  	}
   194  }
   195  
   196  func isBadOp(op v3.Op) bool { return op.Rev() > 0 || len(op.RangeBytes()) > 0 }
   197  
   198  func (lc *leaseCache) Get(ctx context.Context, op v3.Op) (*v3.GetResponse, bool) {
   199  	if isBadOp(op) {
   200  		return nil, false
   201  	}
   202  	key := string(op.KeyBytes())
   203  	li, wc := lc.notify(key)
   204  	if li == nil {
   205  		return nil, true
   206  	}
   207  	select {
   208  	case <-wc:
   209  	case <-ctx.Done():
   210  		return nil, true
   211  	}
   212  	lc.mu.RLock()
   213  	lk := *li
   214  	ret := lk.get(op)
   215  	lc.mu.RUnlock()
   216  	return ret, true
   217  }
   218  
   219  func (lk *leaseKey) get(op v3.Op) *v3.GetResponse {
   220  	ret := *lk.response
   221  	ret.Header = copyHeader(ret.Header)
   222  	empty := len(ret.Kvs) == 0 || op.IsCountOnly()
   223  	empty = empty || (op.MinModRev() > ret.Kvs[0].ModRevision)
   224  	empty = empty || (op.MaxModRev() != 0 && op.MaxModRev() < ret.Kvs[0].ModRevision)
   225  	empty = empty || (op.MinCreateRev() > ret.Kvs[0].CreateRevision)
   226  	empty = empty || (op.MaxCreateRev() != 0 && op.MaxCreateRev() < ret.Kvs[0].CreateRevision)
   227  	if empty {
   228  		ret.Kvs = nil
   229  	} else {
   230  		kv := *ret.Kvs[0]
   231  		kv.Key = make([]byte, len(kv.Key))
   232  		copy(kv.Key, ret.Kvs[0].Key)
   233  		if !op.IsKeysOnly() {
   234  			kv.Value = make([]byte, len(kv.Value))
   235  			copy(kv.Value, ret.Kvs[0].Value)
   236  		}
   237  		ret.Kvs = []*mvccpb.KeyValue{&kv}
   238  	}
   239  	return &ret
   240  }
   241  
   242  func (lc *leaseCache) notify(key string) (*leaseKey, <-chan struct{}) {
   243  	lc.mu.RLock()
   244  	defer lc.mu.RUnlock()
   245  	if li := lc.entries[key]; li != nil {
   246  		return li, li.waitc
   247  	}
   248  	return nil, nil
   249  }
   250  
   251  func (lc *leaseCache) clearOldRevokes(ctx context.Context) {
   252  	for {
   253  		select {
   254  		case <-ctx.Done():
   255  			return
   256  		case <-time.After(time.Second):
   257  			lc.mu.Lock()
   258  			for k, lr := range lc.revokes {
   259  				if time.Now().Sub(lr.Add(revokeBackoff)) > 0 {
   260  					delete(lc.revokes, k)
   261  				}
   262  			}
   263  			lc.mu.Unlock()
   264  		}
   265  	}
   266  }
   267  
   268  func (lc *leaseCache) evalCmp(cmps []v3.Cmp) (cmpVal bool, ok bool) {
   269  	for _, cmp := range cmps {
   270  		if len(cmp.RangeEnd) > 0 {
   271  			return false, false
   272  		}
   273  		lk := lc.entries[string(cmp.Key)]
   274  		if lk == nil {
   275  			return false, false
   276  		}
   277  		if !evalCmp(lk.response, cmp) {
   278  			return false, true
   279  		}
   280  	}
   281  	return true, true
   282  }
   283  
   284  func (lc *leaseCache) evalOps(ops []v3.Op) ([]*v3pb.ResponseOp, bool) {
   285  	resps := make([]*v3pb.ResponseOp, len(ops))
   286  	for i, op := range ops {
   287  		if !op.IsGet() || isBadOp(op) {
   288  			// TODO: support read-only Txn
   289  			return nil, false
   290  		}
   291  		lk := lc.entries[string(op.KeyBytes())]
   292  		if lk == nil {
   293  			return nil, false
   294  		}
   295  		resp := lk.get(op)
   296  		if resp == nil {
   297  			return nil, false
   298  		}
   299  		resps[i] = &v3pb.ResponseOp{
   300  			Response: &v3pb.ResponseOp_ResponseRange{
   301  				(*v3pb.RangeResponse)(resp),
   302  			},
   303  		}
   304  	}
   305  	return resps, true
   306  }