11package cache
22
33import (
4+ "context"
5+ "encoding/json"
46 "strings"
57 "time"
68
79 goCache "github.com/patrickmn/go-cache"
10+ "github.com/redis/go-redis/v9"
811 "github.com/spf13/viper"
9- messagebus "github.com/vardius/message-bus"
1012 "golang.org/x/exp/maps"
1113
1214 "bdo-rest-api/models"
@@ -19,60 +21,65 @@ type CacheEntry[T any] struct {
1921 Status int `json:"status"`
2022}
2123
22- type cache [T any ] struct {
23- Bus messagebus.MessageBus
24- internalCache * goCache.Cache
24+ type Cache [T any ] interface {
25+ AddRecord (keys []string , data T , status int , taskId string ) (date string , expires string )
26+ GetRecord (keys []string ) (data T , status int , date string , expires string , found bool )
27+ GetItemCount () int
28+ GetKeys () []string
29+ GetValues () []CacheEntry [T ]
2530}
2631
2732func joinKeys (keys []string ) string {
2833 return strings .Join (keys , "," )
2934}
3035
31- func newCache [T any ]() * cache [T ] {
32- cacheTTL := viper .GetDuration ("cachettl" )
36+ type memoryCache [T any ] struct {
37+ internalCache * goCache.Cache
38+ ttl time.Duration
39+ }
40+
41+ func newMemoryCache [T any ]() * memoryCache [T ] {
42+ ttl := viper .GetDuration ("cachettl" )
3343
34- return & cache [T ]{
35- Bus : messagebus .New (100 ), // Idk what buffer size is optimal
36- internalCache : goCache . New ( cacheTTL , min ( time . Hour , cacheTTL )) ,
44+ return & memoryCache [T ]{
45+ internalCache : goCache .New (ttl , min ( time . Hour , ttl )),
46+ ttl : ttl ,
3747 }
3848}
3949
40- func (c * cache [T ]) AddRecord (keys []string , data T , status int , taskId string ) (date string , expires string ) {
41- cacheTTL := viper .GetDuration ("cachettl" )
50+ func (c * memoryCache [T ]) AddRecord (keys []string , data T , status int , taskId string ) (date , expires string ) {
4251 entry := CacheEntry [T ]{
4352 Data : data ,
4453 Date : time .Now (),
4554 Status : status ,
4655 }
4756
48- c .internalCache .Add (joinKeys (keys ), entry , cacheTTL )
49- c .Bus .Publish (taskId , entry )
57+ c .internalCache .Add (joinKeys (keys ), entry , c .ttl )
5058
51- return utils .FormatDateForHeaders (entry .Date ), utils .FormatDateForHeaders (entry .Date .Add (cacheTTL ))
59+ return utils .FormatDateForHeaders (entry .Date ), utils .FormatDateForHeaders (entry .Date .Add (c . ttl ))
5260}
5361
54- func (c * cache [T ]) GetRecord (keys []string ) (data T , status int , date string , expires string , found bool ) {
55- cacheTTL := viper .GetDuration ("cachettl" )
56- anyEntry , found := c .internalCache .Get (joinKeys (keys ))
62+ func (c * memoryCache [T ]) GetRecord (keys []string ) (data T , status int , date , expires string , found bool ) {
63+ anyEntry , exp , found := c .internalCache .GetWithExpiration (joinKeys (keys ))
5764
5865 if ! found {
5966 return
6067 }
6168
6269 entry := anyEntry .(CacheEntry [T ])
6370
64- return entry .Data , entry .Status , utils .FormatDateForHeaders (entry .Date ), utils .FormatDateForHeaders (entry . Date . Add ( cacheTTL )), found
71+ return entry .Data , entry .Status , utils .FormatDateForHeaders (entry .Date ), utils .FormatDateForHeaders (exp ), true
6572}
6673
67- func (c * cache [T ]) GetItemCount () int {
74+ func (c * memoryCache [T ]) GetItemCount () int {
6875 return c .internalCache .ItemCount ()
6976}
7077
71- func (c * cache [T ]) GetKeys () []string {
78+ func (c * memoryCache [T ]) GetKeys () []string {
7279 return maps .Keys (c .internalCache .Items ())
7380}
7481
75- func (c * cache [T ]) GetValues () []CacheEntry [T ] {
82+ func (c * memoryCache [T ]) GetValues () []CacheEntry [T ] {
7683 items := c .internalCache .Items ()
7784 result := make ([]CacheEntry [T ], 0 , len (items ))
7885
@@ -83,7 +90,107 @@ func (c *cache[T]) GetValues() []CacheEntry[T] {
8390 return result
8491}
8592
86- var GuildProfiles = newCache [models.GuildProfile ]()
87- var GuildSearch = newCache [[]models.GuildProfile ]()
88- var Profiles = newCache [models.Profile ]()
89- var ProfileSearch = newCache [[]models.Profile ]()
93+ type redisCache [T any ] struct {
94+ client * redis.Client
95+ ctx context.Context
96+ namespace string
97+ ttl time.Duration
98+ }
99+
100+ func newRedisCache [T any ](client * redis.Client , namespace string ) * redisCache [T ] {
101+ return & redisCache [T ]{
102+ client : client ,
103+ ctx : context .Background (),
104+ namespace : namespace + ":" ,
105+ ttl : viper .GetDuration ("cachettl" ),
106+ }
107+ }
108+
109+ func (c * redisCache [T ]) AddRecord (keys []string , data T , status int , taskId string ) (date , expires string ) {
110+ entry := CacheEntry [T ]{
111+ Data : data ,
112+ Date : time .Now (),
113+ Status : status ,
114+ }
115+
116+ b , _ := json .Marshal (entry )
117+ c .client .Set (c .ctx , c .namespace + joinKeys (keys ), b , c .ttl )
118+
119+ return utils .FormatDateForHeaders (entry .Date ), utils .FormatDateForHeaders (entry .Date .Add (c .ttl ))
120+ }
121+
122+ func (c * redisCache [T ]) GetRecord (keys []string ) (data T , status int , date string , expires string , found bool ) {
123+ val , err := c .client .Get (c .ctx , c .namespace + joinKeys (keys )).Bytes ()
124+ if err != nil {
125+ return
126+ }
127+
128+ var entry CacheEntry [T ]
129+ if err := json .Unmarshal (val , & entry ); err != nil {
130+ return
131+ }
132+
133+ ttl := c .client .TTL (c .ctx , c .namespace + joinKeys (keys )).Val ()
134+
135+ return entry .Data , entry .Status , utils .FormatDateForHeaders (entry .Date ), utils .FormatDateForHeaders (time .Now ().Add (ttl )), true
136+ }
137+
138+ func (c * redisCache [T ]) GetItemCount () int {
139+ keys , err := c .client .Keys (c .ctx , c .namespace + "*" ).Result ()
140+ if err != nil {
141+ return 0
142+ }
143+ return len (keys )
144+ }
145+
146+ func (c * redisCache [T ]) GetKeys () []string {
147+ keys , _ := c .client .Keys (c .ctx , c .namespace + "*" ).Result ()
148+
149+ // Remove namespace from keys
150+ for i , k := range keys {
151+ keys [i ] = strings .TrimPrefix (k , c .namespace )
152+ }
153+
154+ return keys
155+ }
156+
157+ func (c * redisCache [T ]) GetValues () []CacheEntry [T ] {
158+ keys , _ := c .client .Keys (c .ctx , c .namespace + "*" ).Result ()
159+ result := make ([]CacheEntry [T ], 0 , len (keys ))
160+
161+ for _ , k := range keys {
162+ val , err := c .client .Get (c .ctx , k ).Bytes ()
163+ if err != nil {
164+ continue
165+ }
166+
167+ var entry CacheEntry [T ]
168+ if err := json .Unmarshal (val , & entry ); err != nil {
169+ continue
170+ }
171+ result = append (result , entry )
172+ }
173+
174+ return result
175+ }
176+
177+ var (
178+ GuildProfiles Cache [models.GuildProfile ]
179+ GuildSearch Cache [[]models.GuildProfile ]
180+ Profiles Cache [models.Profile ]
181+ ProfileSearch Cache [[]models.Profile ]
182+ )
183+
184+ func InitCache () {
185+ if redisClient , err := newRedisClient (viper .GetString ("redis" )); err == nil {
186+ GuildProfiles = newRedisCache [models.GuildProfile ](redisClient , "gpc" )
187+ GuildSearch = newRedisCache [[]models.GuildProfile ](redisClient , "gsc" )
188+ Profiles = newRedisCache [models.Profile ](redisClient , "pc" )
189+ ProfileSearch = newRedisCache [[]models.Profile ](redisClient , "psc" )
190+ } else {
191+ GuildProfiles = newMemoryCache [models.GuildProfile ]()
192+ GuildSearch = newMemoryCache [[]models.GuildProfile ]()
193+ Profiles = newMemoryCache [models.Profile ]()
194+ ProfileSearch = newMemoryCache [[]models.Profile ]()
195+ }
196+ }
0 commit comments