Codebase list golang-github-renekroon-ttlcache / 732cdbd
New upstream release. Debian Janitor 2 years ago
8 changed file(s) with 248 addition(s) and 70 deletion(s). Raw diff Collapse all Expand all
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 {
401403
402404 // SetExpirationCallback sets a callback that will be called when an item expires
403405 func (cache *Cache) SetExpirationCallback(callback ExpireCallback) {
406 cache.mutex.Lock()
407 defer cache.mutex.Unlock()
404408 cache.expireCallback = callback
405409 }
406410
407411 // SetExpirationReasonCallback sets a callback that will be called when an item expires, includes reason of expiry
408412 func (cache *Cache) SetExpirationReasonCallback(callback ExpireReasonCallback) {
413 cache.mutex.Lock()
414 defer cache.mutex.Unlock()
409415 cache.expireReasonCallback = callback
410416 }
411417
412418 // SetCheckExpirationCallback sets a callback that will be called when an item is about to expire
413419 // in order to allow external code to decide whether the item expires or remains for another TTL cycle
414420 func (cache *Cache) SetCheckExpirationCallback(callback CheckExpireCallback) {
421 cache.mutex.Lock()
422 defer cache.mutex.Unlock()
415423 cache.checkExpireCallback = callback
416424 }
417425
418426 // SetNewItemCallback sets a callback that will be called when a new item is added to the cache
419427 func (cache *Cache) SetNewItemCallback(callback ExpireCallback) {
428 cache.mutex.Lock()
429 defer cache.mutex.Unlock()
420430 cache.newItemCallback = callback
421431 }
422432
424434 // no longer extend TTL of items when they are retrieved using Get, or when their expiration condition is evaluated
425435 // using SetCheckExpirationCallback.
426436 func (cache *Cache) SkipTTLExtensionOnHit(value bool) {
437 cache.mutex.Lock()
438 defer cache.mutex.Unlock()
427439 cache.skipTTLExtension = value
428440 }
429441
430442 // SetLoaderFunction allows you to set a function to retrieve cache misses. The signature matches that of the Get function.
431443 // Additional Get calls on the same key block while fetching is in progress (groupcache style).
432444 func (cache *Cache) SetLoaderFunction(loader LoaderFunction) {
445 cache.mutex.Lock()
446 defer cache.mutex.Unlock()
433447 cache.loaderFunction = loader
434448 }
435449
450464 // If a new item is getting cached, the closes item to being timed out will be replaced
451465 // Set to 0 to turn off
452466 func (cache *Cache) SetCacheSizeLimit(limit int) {
467 cache.mutex.Lock()
468 defer cache.mutex.Unlock()
453469 cache.sizeLimit = limit
454470 }
455471
460476
461477 cache := &Cache{
462478 items: make(map[string]*item),
463 loaderLock: make(map[string]*sync.Cond),
479 loaderLock: &singleflight.Group{},
464480 priorityQueue: newPriorityQueue(),
465481 expirationNotification: make(chan bool),
466482 expirationTime: time.Now(),
1717
1818 func TestMain(m *testing.M) {
1919 goleak.VerifyTestMain(m)
20 }
21
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")
2071 }
2172
2273 // Issue #38: Feature request: ability to know why an expiry has occurred
224275
225276 cache.Close()
226277
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()
227309 }
228310
229311 // Issue #28: call expirationCallback automatically on cache.Close()
0 golang-github-renekroon-ttlcache (2.7.0-1) UNRELEASED; urgency=low
1
2 * New upstream release.
3
4 -- Debian Janitor <janitor@jelmer.uk> Thu, 03 Jun 2021 21:33:27 -0000
5
06 golang-github-renekroon-ttlcache (2.3.0+ds-1) unstable; urgency=medium
17
28 * New upstream release.
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=