Package list golang-github-renekroon-ttlcache / 881e1861-82ae-40fe-b495-618bf342159d/upstream
Import upstream version 2.7.0 Debian Janitor 3 months ago
9 changed file(s) with 349 addition(s) and 71 deletion(s). Raw diff Collapse all Expand all
00 language: go
11
22 go:
3 - "1.16.x"
34 - "1.15.x"
4 - "1.14.x"
55
66 git:
77 depth: 1
0 # 2.7.0 (June 2021)
1
2 #46 : got panic
3
4 A panic occured in a line that checks the maximum amount of items in the cache. While not definite root cause has been found, there is indeed the possibility of crashing an empty cache if the cache limit is set to 'zero' which codes for infinite. This would lead to removal of the first item in the cache which would panic on an empty cache.
5
6 Fixed this by applying the global cache lock to all configuration options as well.
7
8 # 2.6.0 (May 2021)
9
10 #44 : There are no API changes, but a contribution was made to use https://pkg.go.dev/golang.org/x/sync/singleflight as a way to provide everybody waiting for a key with that key when it's fetched.
11
12 This removes some complexity from the code and will make sure that all callers will get a return value even if there's high concurrency and low TTL (as proven by the test that was added).
13
14 # 2.5.0 (May 2021)
15
16 ## API changes:
17
18 * #39 : Allow custom loader function for each key via `GetByLoader`
19
20 Introduce the `SimpleCache` interface for quick-start and basic usage.
21
22 # 2.4.0 (April 2021)
23
24 ## API changes:
25
26 * #42 : Add option to get list of keys
27 * #40: Allow 'Touch' on items without other operation
28
29 // Touch resets the TTL of the key when it exists, returns ErrNotFound if the key is not present.
30 func (cache *Cache) Touch(key string) error
31
32 // GetKeys returns all keys of items in the cache. Returns nil when the cache has been closed.
33 func (cache *Cache) GetKeys() []string
34
035 # 2.3.0 (February 2021)
136
237 ## API changes:
2222
2323 ## Usage
2424
25 You can copy it as a full standalone demo program.
25 You can copy it as a full standalone demo program. The first snippet is basic usage, where the second exploits more options in the cache.
2626
27 Basic:
28 ```go
29 package main
30
31 import (
32 "fmt"
33 "time"
34
35 "github.com/ReneKroon/ttlcache/v2"
36 )
37
38 var notFound = ttlcache.ErrNotFound
39
40 func main() {
41 var cache ttlcache.SimpleCache = ttlcache.NewCache()
42
43 cache.SetTTL(time.Duration(10 * time.Second))
44 cache.Set("MyKey", "MyValue")
45 cache.Set("MyNumber", 1000)
46
47 if val, err := cache.Get("MyKey"); err != notFound {
48 fmt.Printf("Got it: %s\n", val)
49 }
50
51 cache.Remove("MyNumber")
52 cache.Purge()
53 cache.Close()
54 }
55 ```
56
57 Advanced:
2758 ```go
2859 package main
2960
0 package bench
1
2 import (
3 "fmt"
4 "testing"
5 "time"
6
7 ttlcache "github.com/ReneKroon/ttlcache/v2"
8 )
9
10 func BenchmarkCacheSetWithoutTTL(b *testing.B) {
11 cache := ttlcache.NewCache()
12 defer cache.Close()
13
14 for n := 0; n < b.N; n++ {
15 cache.Set(fmt.Sprint(n%1000000), "value")
16 }
17 }
18
19 func BenchmarkCacheSetWithGlobalTTL(b *testing.B) {
20 cache := ttlcache.NewCache()
21 defer cache.Close()
22
23 cache.SetTTL(time.Duration(50 * time.Millisecond))
24 for n := 0; n < b.N; n++ {
25 cache.Set(fmt.Sprint(n%1000000), "value")
26 }
27 }
28
29 func BenchmarkCacheSetWithTTL(b *testing.B) {
30 cache := ttlcache.NewCache()
31 defer cache.Close()
32
33 for n := 0; n < b.N; n++ {
34 cache.SetWithTTL(fmt.Sprint(n%1000000), "value", time.Duration(50*time.Millisecond))
35 }
36 }
00 package ttlcache
11
22 import (
3 "golang.org/x/sync/singleflight"
34 "sync"
45 "time"
56 )
1617
1718 // LoaderFunction can be supplied to retrieve an item where a cache miss occurs. Supply an item specific ttl or Duration.Zero
1819 type LoaderFunction func(key string) (data interface{}, ttl time.Duration, err error)
20
21 // SimpleCache interface enables a quick-start. Interface for basic usage.
22 type SimpleCache interface {
23 Get(key string) (interface{}, error)
24 Set(key string, data interface{}) error
25 SetTTL(ttl time.Duration) error
26 SetWithTTL(key string, data interface{}, ttl time.Duration) error
27 Remove(key string) error
28 Close() error
29 Purge() error
30 }
1931
2032 // Cache is a synchronized map of items that can auto-expire once stale
2133 type Cache struct {
2234 mutex sync.Mutex
2335 ttl time.Duration
2436 items map[string]*item
25 loaderLock map[string]*sync.Cond
37 loaderLock *singleflight.Group
2638 expireCallback ExpireCallback
2739 expireReasonCallback ExpireReasonCallback
2840 checkExpireCallback CheckExpireCallback
265277 // Get is a thread-safe way to lookup items
266278 // Every lookup, also touches the item, hence extending it's life
267279 func (cache *Cache) Get(key string) (interface{}, error) {
280 return cache.GetByLoader(key, nil)
281 }
282
283 // GetByLoader can take a per key loader function (ie. to propagate context)
284 func (cache *Cache) GetByLoader(key string, customLoaderFunction LoaderFunction) (interface{}, error) {
268285 cache.mutex.Lock()
269286 if cache.isShutDown {
270287 cache.mutex.Unlock()
285302 cache.metrics.Misses++
286303 err = ErrNotFound
287304 }
288 if cache.loaderFunction == nil || exists {
289 cache.mutex.Unlock()
290 }
291
292 if cache.loaderFunction != nil && !exists {
293 if lock, ok := cache.loaderLock[key]; ok {
294 // if a lock is present then a fetch is in progress and we wait.
295 cache.mutex.Unlock()
296 lock.L.Lock()
297 lock.Wait()
298 lock.L.Unlock()
299 cache.mutex.Lock()
300 item, exists, triggerExpirationNotification = cache.getItem(key)
301 if exists {
302 dataToReturn = item.data
303 err = nil
304 }
305 cache.mutex.Unlock()
306 } else {
307 // if no lock is present we are the leader and should set the lock and fetch.
308 m := sync.NewCond(&sync.Mutex{})
309 cache.loaderLock[key] = m
310 cache.mutex.Unlock()
311 // cache is not blocked during IO
312 dataToReturn, err = cache.invokeLoader(key)
313 cache.mutex.Lock()
314 m.Broadcast()
315 // cleanup so that we don't block consecutive access.
316 delete(cache.loaderLock, key)
317 cache.mutex.Unlock()
318 }
319
305
306 loaderFunction := cache.loaderFunction
307 if customLoaderFunction != nil {
308 loaderFunction = customLoaderFunction
309 }
310
311 if loaderFunction == nil || exists {
312 cache.mutex.Unlock()
313 }
314
315 if loaderFunction != nil && !exists {
316 cache.mutex.Unlock()
317 dataToReturn, err, _ = cache.loaderLock.Do(key, func() (interface{}, error) {
318 // cache is not blocked during io
319 invokeData, err := cache.invokeLoader(key, loaderFunction)
320 return invokeData, err
321 })
320322 }
321323
322324 if triggerExpirationNotification {
326328 return dataToReturn, err
327329 }
328330
329 func (cache *Cache) invokeLoader(key string) (dataToReturn interface{}, err error) {
331 func (cache *Cache) invokeLoader(key string, loaderFunction LoaderFunction) (dataToReturn interface{}, err error) {
330332 var ttl time.Duration
331333
332 dataToReturn, ttl, err = cache.loaderFunction(key)
334 dataToReturn, ttl, err = loaderFunction(key)
333335 if err == nil {
334336 err = cache.SetWithTTL(key, dataToReturn, ttl)
335337 if err != nil {
368370 return length
369371 }
370372
373 // GetKeys returns all keys of items in the cache. Returns nil when the cache has been closed.
374 func (cache *Cache) GetKeys() []string {
375 cache.mutex.Lock()
376 defer cache.mutex.Unlock()
377
378 if cache.isShutDown {
379 return nil
380 }
381 keys := make([]string, len(cache.items))
382 i := 0
383 for k := range cache.items {
384 keys[i] = k
385 i++
386 }
387 return keys
388 }
389
371390 // SetTTL sets the global TTL value for items in the cache, which can be overridden at the item level.
372391 func (cache *Cache) SetTTL(ttl time.Duration) error {
373392 cache.mutex.Lock()
384403
385404 // SetExpirationCallback sets a callback that will be called when an item expires
386405 func (cache *Cache) SetExpirationCallback(callback ExpireCallback) {
406 cache.mutex.Lock()
407 defer cache.mutex.Unlock()
387408 cache.expireCallback = callback
388409 }
389410
390411 // SetExpirationReasonCallback sets a callback that will be called when an item expires, includes reason of expiry
391412 func (cache *Cache) SetExpirationReasonCallback(callback ExpireReasonCallback) {
413 cache.mutex.Lock()
414 defer cache.mutex.Unlock()
392415 cache.expireReasonCallback = callback
393416 }
394417
395418 // SetCheckExpirationCallback sets a callback that will be called when an item is about to expire
396419 // in order to allow external code to decide whether the item expires or remains for another TTL cycle
397420 func (cache *Cache) SetCheckExpirationCallback(callback CheckExpireCallback) {
421 cache.mutex.Lock()
422 defer cache.mutex.Unlock()
398423 cache.checkExpireCallback = callback
399424 }
400425
401426 // SetNewItemCallback sets a callback that will be called when a new item is added to the cache
402427 func (cache *Cache) SetNewItemCallback(callback ExpireCallback) {
428 cache.mutex.Lock()
429 defer cache.mutex.Unlock()
403430 cache.newItemCallback = callback
404431 }
405432
407434 // no longer extend TTL of items when they are retrieved using Get, or when their expiration condition is evaluated
408435 // using SetCheckExpirationCallback.
409436 func (cache *Cache) SkipTTLExtensionOnHit(value bool) {
437 cache.mutex.Lock()
438 defer cache.mutex.Unlock()
410439 cache.skipTTLExtension = value
411440 }
412441
413442 // SetLoaderFunction allows you to set a function to retrieve cache misses. The signature matches that of the Get function.
414443 // Additional Get calls on the same key block while fetching is in progress (groupcache style).
415444 func (cache *Cache) SetLoaderFunction(loader LoaderFunction) {
445 cache.mutex.Lock()
446 defer cache.mutex.Unlock()
416447 cache.loaderFunction = loader
417448 }
418449
433464 // If a new item is getting cached, the closes item to being timed out will be replaced
434465 // Set to 0 to turn off
435466 func (cache *Cache) SetCacheSizeLimit(limit int) {
467 cache.mutex.Lock()
468 defer cache.mutex.Unlock()
436469 cache.sizeLimit = limit
437470 }
438471
443476
444477 cache := &Cache{
445478 items: make(map[string]*item),
446 loaderLock: make(map[string]*sync.Cond),
479 loaderLock: &singleflight.Group{},
447480 priorityQueue: newPriorityQueue(),
448481 expirationNotification: make(chan bool),
449482 expirationTime: time.Now(),
464497 return cache.metrics
465498 }
466499
500 // Touch resets the TTL of the key when it exists, returns ErrNotFound if the key is not present.
501 func (cache *Cache) Touch(key string) error {
502 cache.mutex.Lock()
503 defer cache.mutex.Unlock()
504 item, exists := cache.items[key]
505 if !exists {
506 return ErrNotFound
507 }
508 item.touch()
509 return nil
510 }
511
467512 func min(duration time.Duration, second time.Duration) time.Duration {
468513 if duration < second {
469514 return duration
1919 goleak.VerifyTestMain(m)
2020 }
2121
22 // The SimpleCache interface enables quick-start.
23 func TestCache_SimpleCache(t *testing.T) {
24 t.Parallel()
25 var cache SimpleCache = NewCache()
26
27 cache.SetTTL(time.Second)
28 cache.Set("k", "v")
29 cache.Get("k")
30 cache.Purge()
31 cache.Close()
32
33 }
34
35 // Issue / PR #39: add customer loader function for each Get() #
36 // some middleware prefers to define specific context's etc per Get.
37 // This is faciliated by supplying a loder function with Get's.
38 func TestCache_GetByLoader(t *testing.T) {
39 t.Parallel()
40 cache := NewCache()
41 defer cache.Close()
42
43 globalLoader := func(key string) (data interface{}, ttl time.Duration, err error) {
44 return "global", 0, nil
45 }
46 cache.SetLoaderFunction(globalLoader)
47
48 localLoader := func(key string) (data interface{}, ttl time.Duration, err error) {
49 return "local", 0, nil
50 }
51
52 key, _ := cache.Get("test")
53 assert.Equal(t, "global", key)
54
55 cache.Remove("test")
56
57 localKey, _ := cache.GetByLoader("test", localLoader)
58 assert.Equal(t, "local", localKey)
59
60 cache.Remove("test")
61
62 globalKey, _ := cache.GetByLoader("test", globalLoader)
63 assert.Equal(t, "global", globalKey)
64
65 cache.Remove("test")
66
67 defaultKey, _ := cache.GetByLoader("test", nil)
68 assert.Equal(t, "global", defaultKey)
69
70 cache.Remove("test")
71 }
72
2273 // Issue #38: Feature request: ability to know why an expiry has occurred
2374 func TestCache_textExpirationReasons(t *testing.T) {
2475 t.Parallel()
54105 <-sync
55106 assert.Equal(t, Closed, reason)
56107
108 }
109
110 func TestCache_TestTouch(t *testing.T) {
111 t.Parallel()
112 cache := NewCache()
113 defer cache.Close()
114
115 lock := sync.Mutex{}
116
117 lock.Lock()
118 expired := false
119 lock.Unlock()
120
121 cache.SkipTTLExtensionOnHit(true)
122 cache.SetExpirationCallback(func(key string, value interface{}) {
123 lock.Lock()
124 defer lock.Unlock()
125 expired = true
126 })
127
128 cache.SetWithTTL("key", "data", time.Millisecond*900)
129 <-time.After(time.Millisecond * 500)
130
131 // no Touch
132 // cache.Touch("key")
133
134 <-time.After(time.Millisecond * 500)
135 lock.Lock()
136 assert.Equal(t, true, expired)
137 lock.Unlock()
138 cache.Remove("key")
139
140 lock.Lock()
141 expired = false
142 lock.Unlock()
143
144 cache.SetWithTTL("key", "data", time.Millisecond*900)
145 <-time.After(time.Millisecond * 500)
146 cache.Touch("key")
147
148 <-time.After(time.Millisecond * 500)
149 lock.Lock()
150 assert.Equal(t, false, expired)
151 lock.Unlock()
57152 }
58153
59154 // Issue #37: Cache metrics
180275
181276 cache.Close()
182277
278 }
279
280 // Cache sometimes returns key not found under parallel access with a loader function
281 func TestCache_TestLoaderFunctionParallelKeyAccess(t *testing.T) {
282 t.Parallel()
283 cache := NewCache()
284
285 cache.SetLoaderFunction(func(key string) (data interface{}, ttl time.Duration, err error) {
286 time.Sleep(time.Millisecond * 300)
287 return "1", 1 * time.Nanosecond, nil
288 })
289
290 wg := sync.WaitGroup{}
291 errCount := uint64(0)
292 for i := 0; i < 200; i++ {
293 wg.Add(1)
294 go func() {
295 defer wg.Done()
296 value, found := cache.Get("foo")
297 if value != "1" || found != nil { // Use an atomic to avoid spamming logs
298 atomic.AddUint64(&errCount, 1)
299 }
300 }()
301
302 }
303
304 wg.Wait()
305
306 assert.Equalf(t, uint64(0), errCount, "expected 0 errs, got %d", errCount)
307
308 cache.Close()
183309 }
184310
185311 // Issue #28: call expirationCallback automatically on cache.Close()
610736 assert.Equal(t, "world", (data.(string)), "Expected data content to be 'world'")
611737 }
612738
739 func TestCacheGetKeys(t *testing.T) {
740 t.Parallel()
741
742 cache := NewCache()
743 defer cache.Close()
744
745 keys := cache.GetKeys()
746 assert.Empty(t, keys, "Expected keys to be empty")
747
748 cache.Set("hello", "world")
749 keys = cache.GetKeys()
750 assert.NotEmpty(t, keys, "Expected keys to be not empty")
751 assert.Equal(t, []string{"hello"}, keys, "Expected keys contains 'hello'")
752 }
753
613754 func TestCacheExpirationCallbackFunction(t *testing.T) {
614755 t.Parallel()
615756
0 package ttlcache
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestError(t *testing.T) {
9 assert.Equal(t, "key not found", ErrNotFound.Error())
10
11 }
12
13 func TestEvictionError(t *testing.T) {
14 assert.Equal(t, "Removed", Removed.String())
15 assert.Equal(t, "Expired", Expired.String())
16
17 }
22 go 1.15
33
44 require (
5 github.com/alvaroloes/enumer v1.1.2 // indirect
65 github.com/davecgh/go-spew v1.1.1 // indirect
76 github.com/stretchr/testify v1.7.0
87 go.uber.org/goleak v1.1.10
98 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
9 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
1010 golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 // indirect
1111 )
0 github.com/alvaroloes/enumer v1.1.2 h1:5khqHB33TZy1GWCO/lZwcroBFh7u+0j40T83VUbfAMY=
1 github.com/alvaroloes/enumer v1.1.2/go.mod h1:FxrjvuXoDAx9isTJrv4c+T410zFi0DtXIT0m65DJ+Wo=
2 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
3 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
40 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
51 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
62 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
73 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
84 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
9 github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
10 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
115 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
126 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
137 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
14 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
15 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
16 github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1 h1:/I3lTljEEDNYLho3/FUB7iD/oc2cEFgVmbHzV+O0PtU=
17 github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1/go.mod h1:eD5JxqMiuNYyFNmyY9rkJ/slN8y59oEu4Ei7F8OoKWQ=
188 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
199 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
20 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
2110 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
2211 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
23 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
24 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
2512 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
2613 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
27 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
2814 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
2915 go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
3016 go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
3117 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3218 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
3319 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
34 golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
3520 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
36 golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
37 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
3821 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
3922 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
4023 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
4124 golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
4225 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
43 golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
44 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
4526 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
4627 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
4728 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
48 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
4929 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
5030 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
51 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
31 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
5232 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
33 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
34 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
5335 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
5436 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
55 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5637 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5738 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
5839 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
5940 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
6041 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
61 golang.org/x/tools v0.0.0-20190524210228-3d17549cdc6b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
62 golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
6342 golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
6443 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
6544 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
66 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d h1:szSOL78iTCl0LF1AMjhSWJj8tIM0KixlUUnBtYXsmd8=
67 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
6845 golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 h1:BmCFkEH4nJrYcAc2L08yX5RhYGD4j58PTMkEUDkpz2I=
6946 golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
7047 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
7148 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
72 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
73 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
7449 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
7550 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
7651 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7752 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
7853 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
79 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
80 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
8154 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
8255 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
8356 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
84 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
85 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=