github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/cache/chain.go (about) 1 package cache 2 3 import ( 4 "fmt" 5 "github.com/angenalZZZ/gofunc/data/cache/store" 6 "github.com/angenalZZZ/gofunc/f" 7 "github.com/panjf2000/ants/v2" 8 "time" 9 ) 10 11 const ( 12 // ChainType represents the chain cache type as a string value 13 ChainType = "chain" 14 ) 15 16 // chainKeyValue transport in the channel 17 type chainKeyValue struct { 18 idx int 19 key string 20 value interface{} 21 options *store.Options 22 } 23 24 // ChainCache represents the configuration needed by a cache aggregator 25 type ChainCache struct { 26 caches []StorageInterface 27 pool *ants.PoolWithFunc 28 } 29 30 // NewChain create a new cache aggregator 31 func NewChain(caches ...StorageInterface) *ChainCache { 32 chain := &ChainCache{caches: caches} 33 34 chain.pool, _ = ants.NewPoolWithFunc(100000, func(payload interface{}) { 35 if payload == nil { 36 return 37 } 38 if set, ok := payload.(*chainKeyValue); ok { 39 _ = chain.caches[set.idx].Set(set.key, set.value, set.options) 40 } 41 }, ants.WithOptions(ants.Options{ 42 ExpiryDuration: ants.DefaultCleanIntervalTime, 43 PreAlloc: true, 44 Nonblocking: true, 45 MaxBlockingTasks: 0, 46 PanicHandler: func(err interface{}) { 47 _ = fmt.Errorf(" GoHttpHandle/worker: %s\n %v", f.Now().LocalTimeString(), err) 48 }, 49 })) 50 return chain 51 } 52 53 // Get returns the value stored in cache if it exists 54 func (c *ChainCache) Get(key string) (value interface{}, err error) { 55 for i, cache := range c.caches { 56 value, err = cache.Get(key) 57 if err == nil { 58 // Set the value back until this cache layer 59 if i == 0 { 60 return value, nil 61 } 62 option, err := cache.TTL(key) 63 if err != nil { 64 return value, nil 65 } 66 for ; i >= 0; i-- { 67 if err = c.pool.Invoke(&chainKeyValue{ 68 idx: i, 69 key: key, 70 value: value, 71 options: &store.Options{ 72 Expiration: option, 73 }, 74 }); err != nil { 75 _ = fmt.Errorf("unable to set item into cache with store '%s': %v", cache.GetCodec().GetStore().GetType(), err) 76 continue 77 } 78 } 79 return value, nil 80 } 81 } 82 return 83 } 84 85 // Set sets a value in available caches 86 func (c *ChainCache) Set(key string, value interface{}, options *store.Options) error { 87 for i, cache := range c.caches { 88 if i == 0 && options.Async == false { 89 err := cache.Set(key, value, options) 90 if err != nil { 91 storeType := cache.GetCodec().GetStore().GetType() 92 return fmt.Errorf("unable to set item into cache with store '%s': %v", storeType, err) 93 } 94 } else { 95 if err := c.pool.Invoke(&chainKeyValue{ 96 idx: i, 97 key: key, 98 value: value, 99 options: options, 100 }); err != nil { 101 storeType := cache.GetCodec().GetStore().GetType() 102 return fmt.Errorf("unable to set item into cache with store '%s': %v", storeType, err) 103 } 104 } 105 } 106 return nil 107 } 108 109 // Delete removes a value from all available caches 110 func (c *ChainCache) Delete(key string) error { 111 for _, cache := range c.caches { 112 _ = cache.Delete(key) 113 } 114 return nil 115 } 116 117 // TTL returns an expiration time 118 func (c *ChainCache) TTL(key string) (time.Duration, error) { 119 return c.caches[len(c.caches)-1].TTL(key) 120 } 121 122 // Invalidate invalidates cache item from given options 123 func (c *ChainCache) Invalidate(options store.InvalidateOptions) error { 124 for _, cache := range c.caches { 125 _ = cache.Invalidate(options) 126 } 127 return nil 128 } 129 130 // Clear resets all cache data 131 func (c *ChainCache) Clear() error { 132 c.pool.Release() 133 for _, cache := range c.caches { 134 _ = cache.Clear() 135 } 136 c.pool.Reboot() 137 return nil 138 } 139 140 // GetCaches returns all chain caches 141 func (c *ChainCache) GetCaches() []StorageInterface { 142 return c.caches 143 } 144 145 // GetType returns the cache type 146 func (c *ChainCache) GetType() string { 147 return ChainType 148 }