github.com/Jeffail/benthos/v3@v3.65.0/public/service/cache.go (about) 1 package service 2 3 import ( 4 "context" 5 "errors" 6 "time" 7 8 "github.com/Jeffail/benthos/v3/internal/component/cache" 9 "github.com/Jeffail/benthos/v3/internal/shutdown" 10 "github.com/Jeffail/benthos/v3/lib/metrics" 11 "github.com/Jeffail/benthos/v3/lib/types" 12 ) 13 14 // Errors returned by cache types. 15 var ( 16 ErrKeyAlreadyExists = errors.New("key already exists") 17 ErrKeyNotFound = errors.New("key does not exist") 18 ) 19 20 // Cache is an interface implemented by Benthos caches. 21 type Cache interface { 22 // Get a cache item. 23 Get(ctx context.Context, key string) ([]byte, error) 24 25 // Set a cache item, specifying an optional TTL. It is okay for caches to 26 // ignore the ttl parameter if it isn't possible to implement. 27 Set(ctx context.Context, key string, value []byte, ttl *time.Duration) error 28 29 // Add is the same operation as Set except that it returns an error if the 30 // key already exists. It is okay for caches to return nil on duplicates if 31 // it isn't possible to implement. 32 Add(ctx context.Context, key string, value []byte, ttl *time.Duration) error 33 34 // Delete attempts to remove a key. If the key does not exist then it is 35 // considered correct to return an error, however, for cache implementations 36 // where it is difficult to determine this then it is acceptable to return 37 // nil. 38 Delete(ctx context.Context, key string) error 39 40 Closer 41 } 42 43 // CacheItem represents an individual cache item. 44 type CacheItem struct { 45 Key string 46 Value []byte 47 TTL *time.Duration 48 } 49 50 // batchedCache represents a cache where the underlying implementation is able 51 // to benefit from batched set requests. This interface is optional for caches 52 // and when implemented will automatically be utilised where possible. 53 type batchedCache interface { 54 // SetMulti attempts to set multiple cache items in as few requests as 55 // possible. 56 SetMulti(ctx context.Context, keyValues ...CacheItem) error 57 } 58 59 //------------------------------------------------------------------------------ 60 61 // Implements types.Cache 62 type airGapCache struct { 63 c Cache 64 cm batchedCache 65 66 sig *shutdown.Signaller 67 } 68 69 func newAirGapCache(c Cache, stats metrics.Type) types.Cache { 70 ag := &airGapCache{c, nil, shutdown.NewSignaller()} 71 ag.cm, _ = c.(batchedCache) 72 return cache.NewV2ToV1Cache(ag, stats) 73 } 74 75 func (a *airGapCache) Get(ctx context.Context, key string) ([]byte, error) { 76 b, err := a.c.Get(ctx, key) 77 if errors.Is(err, ErrKeyNotFound) { 78 err = types.ErrKeyNotFound 79 } 80 return b, err 81 } 82 83 func (a *airGapCache) Set(ctx context.Context, key string, value []byte, ttl *time.Duration) error { 84 return a.c.Set(ctx, key, value, ttl) 85 } 86 87 func (a *airGapCache) SetMulti(ctx context.Context, keyValues map[string]types.CacheTTLItem) error { 88 if a.cm != nil { 89 items := make([]CacheItem, 0, len(keyValues)) 90 for k, v := range keyValues { 91 items = append(items, CacheItem{ 92 Key: k, 93 Value: v.Value, 94 TTL: v.TTL, 95 }) 96 } 97 return a.cm.SetMulti(ctx, items...) 98 } 99 for k, v := range keyValues { 100 if err := a.c.Set(ctx, k, v.Value, v.TTL); err != nil { 101 return err 102 } 103 } 104 return nil 105 } 106 107 func (a *airGapCache) Add(ctx context.Context, key string, value []byte, ttl *time.Duration) error { 108 err := a.c.Add(ctx, key, value, ttl) 109 if errors.Is(err, ErrKeyAlreadyExists) { 110 err = types.ErrKeyAlreadyExists 111 } 112 return err 113 } 114 115 func (a *airGapCache) Delete(ctx context.Context, key string) error { 116 return a.c.Delete(ctx, key) 117 } 118 119 func (a *airGapCache) Close(ctx context.Context) error { 120 return a.c.Close(ctx) 121 } 122 123 //------------------------------------------------------------------------------ 124 125 // Implements Cache around a types.Cache 126 type reverseAirGapCache struct { 127 c types.Cache 128 } 129 130 func newReverseAirGapCache(c types.Cache) *reverseAirGapCache { 131 return &reverseAirGapCache{c} 132 } 133 134 func (r *reverseAirGapCache) Get(ctx context.Context, key string) ([]byte, error) { 135 b, err := r.c.Get(key) 136 if errors.Is(err, types.ErrKeyNotFound) { 137 err = ErrKeyNotFound 138 } 139 return b, err 140 } 141 142 func (r *reverseAirGapCache) Set(ctx context.Context, key string, value []byte, ttl *time.Duration) error { 143 if cttl, ok := r.c.(types.CacheWithTTL); ok { 144 return cttl.SetWithTTL(key, value, ttl) 145 } 146 return r.c.Set(key, value) 147 } 148 149 func (r *reverseAirGapCache) Add(ctx context.Context, key string, value []byte, ttl *time.Duration) (err error) { 150 if cttl, ok := r.c.(types.CacheWithTTL); ok { 151 err = cttl.AddWithTTL(key, value, ttl) 152 } else { 153 err = r.c.Add(key, value) 154 } 155 if errors.Is(err, types.ErrKeyAlreadyExists) { 156 err = ErrKeyAlreadyExists 157 } 158 return 159 } 160 161 func (r *reverseAirGapCache) Delete(ctx context.Context, key string) error { 162 return r.c.Delete(key) 163 } 164 165 func (r *reverseAirGapCache) Close(ctx context.Context) error { 166 r.c.CloseAsync() 167 for { 168 // Gross but will do for now until we replace these with context params. 169 if err := r.c.WaitForClose(time.Millisecond * 100); err == nil { 170 return nil 171 } 172 select { 173 case <-ctx.Done(): 174 return ctx.Err() 175 default: 176 } 177 } 178 }