github.com/argoproj/argo-cd@v1.8.7/util/cache/redis.go (about) 1 package cache 2 3 import ( 4 "context" 5 "encoding/json" 6 "time" 7 8 ioutil "github.com/argoproj/argo-cd/util/io" 9 10 rediscache "github.com/go-redis/cache/v8" 11 "github.com/go-redis/redis/v8" 12 ) 13 14 func NewRedisCache(client *redis.Client, expiration time.Duration) CacheClient { 15 return &redisCache{ 16 client: client, 17 expiration: expiration, 18 cache: rediscache.New(&rediscache.Options{Redis: client}), 19 } 20 } 21 22 type redisCache struct { 23 expiration time.Duration 24 client *redis.Client 25 cache *rediscache.Cache 26 } 27 28 func (r *redisCache) Set(item *Item) error { 29 expiration := item.Expiration 30 if expiration == 0 { 31 expiration = r.expiration 32 } 33 34 val, err := json.Marshal(item.Object) 35 if err != nil { 36 return err 37 } 38 39 return r.cache.Set(&rediscache.Item{ 40 Key: item.Key, 41 Value: val, 42 TTL: expiration, 43 }) 44 } 45 46 func (r *redisCache) Get(key string, obj interface{}) error { 47 var data []byte 48 err := r.cache.Get(context.TODO(), key, &data) 49 if err == rediscache.ErrCacheMiss { 50 err = ErrCacheMiss 51 } 52 if err != nil { 53 return err 54 } 55 return json.Unmarshal(data, obj) 56 } 57 58 func (r *redisCache) Delete(key string) error { 59 return r.cache.Delete(context.TODO(), key) 60 } 61 62 func (r *redisCache) OnUpdated(ctx context.Context, key string, callback func() error) error { 63 pubsub := r.client.Subscribe(ctx, key) 64 defer ioutil.Close(pubsub) 65 66 ch := pubsub.Channel() 67 for { 68 select { 69 case <-ctx.Done(): 70 return nil 71 case <-ch: 72 if err := callback(); err != nil { 73 return err 74 } 75 } 76 } 77 } 78 79 func (r *redisCache) NotifyUpdated(key string) error { 80 return r.client.Publish(context.TODO(), key, "").Err() 81 } 82 83 type MetricsRegistry interface { 84 IncRedisRequest(failed bool) 85 ObserveRedisRequestDuration(duration time.Duration) 86 } 87 88 var metricStartTimeKey = struct{}{} 89 90 type redisHook struct { 91 registry MetricsRegistry 92 } 93 94 func (rh *redisHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) { 95 return context.WithValue(ctx, metricStartTimeKey, time.Now()), nil 96 } 97 98 func (rh *redisHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error { 99 cmdErr := cmd.Err() 100 rh.registry.IncRedisRequest(cmdErr != nil && cmdErr != redis.Nil) 101 102 startTime := ctx.Value(metricStartTimeKey).(time.Time) 103 duration := time.Since(startTime) 104 rh.registry.ObserveRedisRequestDuration(duration) 105 106 return nil 107 } 108 109 func (redisHook) BeforeProcessPipeline(ctx context.Context, _ []redis.Cmder) (context.Context, error) { 110 return ctx, nil 111 } 112 113 func (redisHook) AfterProcessPipeline(_ context.Context, _ []redis.Cmder) error { 114 return nil 115 } 116 117 // CollectMetrics add transport wrapper that pushes metrics into the specified metrics registry 118 func CollectMetrics(client *redis.Client, registry MetricsRegistry) { 119 client.AddHook(&redisHook{registry: registry}) 120 }