Skip to content

Commit bf86694

Browse files
committed
update add redis cache
1 parent 646667f commit bf86694

File tree

7 files changed

+210
-133
lines changed

7 files changed

+210
-133
lines changed

cache/cache.go

+26-88
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,56 @@
11
package cache
22

33
import (
4-
"sync"
54
"time"
6-
7-
"github.com/ochom/gutils/env"
8-
"github.com/ochom/gutils/logs"
95
)
106

11-
var cacheWorkers int
12-
var memoryCache map[string]cacheItem
13-
var mut sync.Mutex
14-
15-
// V is the type of the value to be stored in the cache
16-
type V []byte
17-
18-
// cacheItem ...
19-
type cacheItem struct {
20-
value []byte
21-
createdAt time.Time
22-
expiry time.Duration
23-
callBack func()
7+
// Cache ...
8+
type Cache interface {
9+
set(key string, value V)
10+
setWithExpiry(key string, value V, expiry time.Duration)
11+
get(key string) V
12+
delete(key string)
13+
cleanUp()
2414
}
2515

26-
// expired ...
27-
func (c cacheItem) expired() bool {
28-
return time.Since(c.createdAt) > c.expiry
29-
}
16+
var conn Cache
17+
18+
// NewCache ...
19+
func Init(driver CacheDriver, url ...string) {
20+
switch driver {
21+
case RedisDriver:
22+
conn = newRedisCache(url...)
23+
case MemoryDriver:
24+
conn = newMemoryCache()
25+
}
3026

31-
func init() {
32-
cacheWorkers = env.Int("CACHE_TOTAL_WORKERS", 10)
33-
memoryCache = make(map[string]cacheItem)
34-
go CleanUp()
27+
go conn.cleanUp()
3528
}
3629

3730
// Set ...
3831
func Set(key string, value V) {
39-
expiry := time.Hour * time.Duration(env.Int("MAX_CACHE_HOUR", 24))
40-
SetWithExpiry(key, value, expiry)
32+
conn.set(key, value)
4133
}
4234

4335
// SetWithExpiry ...
4436
func SetWithExpiry(key string, value V, expiry time.Duration) {
45-
callback := func() {
46-
logs.Info("Session item expired: %s", key)
47-
}
48-
SetWithCallback(key, value, expiry, callback)
49-
}
50-
51-
// SetWithCallback ...
52-
func SetWithCallback(key string, value V, expiry time.Duration, callback func()) {
53-
mut.Lock()
54-
defer mut.Unlock()
55-
item := cacheItem{
56-
value: value,
57-
createdAt: time.Now(),
58-
expiry: expiry,
59-
callBack: callback,
60-
}
61-
memoryCache[key] = item
37+
conn.setWithExpiry(key, value, expiry)
6238
}
6339

6440
// Get ...
6541
func Get(key string) V {
66-
mut.Lock()
67-
defer mut.Unlock()
68-
69-
item, ok := memoryCache[key]
70-
if !ok {
71-
return nil
72-
}
73-
74-
return item.value
42+
return conn.get(key)
7543
}
7644

7745
// Delete ...
7846
func Delete(key string) {
79-
mut.Lock()
80-
defer mut.Unlock()
81-
delete(memoryCache, key)
47+
conn.delete(key)
8248
}
8349

84-
// CleanUp deletes expired cache items and calls their callbacks
50+
// CleanUp ...
8551
func CleanUp() {
86-
jobs := make(chan cacheItem, 100)
87-
88-
// start workers
89-
for i := 0; i < cacheWorkers; i++ {
90-
go runCallbacks(jobs)
91-
}
92-
93-
tick(jobs)
94-
}
95-
96-
// tick to run the ticker
97-
func tick(jobs chan<- cacheItem) {
98-
tk := time.NewTicker(time.Second) // every second
99-
for range tk.C {
100-
// acquire
101-
mut.Lock()
102-
for key, item := range memoryCache {
103-
if item.expired() {
104-
jobs <- item
105-
delete(memoryCache, key)
106-
}
107-
}
108-
109-
// release
110-
mut.Unlock()
111-
}
112-
}
113-
114-
func runCallbacks(jobs <-chan cacheItem) {
115-
for item := range jobs {
116-
item.callBack()
52+
tick := time.NewTicker(time.Second)
53+
for range tick.C {
54+
conn.cleanUp()
11755
}
11856
}

cache/cache_test.go

-45
This file was deleted.

cache/domain.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package cache
2+
3+
import (
4+
"time"
5+
)
6+
7+
// CacheDriver ...
8+
type CacheDriver string
9+
10+
const (
11+
RedisDriver CacheDriver = "redis"
12+
MemoryDriver CacheDriver = "memory"
13+
)
14+
15+
// V is the type of the value to be stored in the cache
16+
type V []byte
17+
18+
// cacheItem ...
19+
type cacheItem struct {
20+
value []byte
21+
createdAt time.Time
22+
expiry time.Duration
23+
}
24+
25+
// expired ...
26+
func (c cacheItem) expired() bool {
27+
return time.Since(c.createdAt) > c.expiry
28+
}

cache/memory.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package cache
2+
3+
import (
4+
"sync"
5+
"time"
6+
7+
"github.com/ochom/gutils/env"
8+
)
9+
10+
// memoryCache implements Cache
11+
type memoryCache struct {
12+
cacheWorkers int
13+
items map[string]cacheItem
14+
mut sync.Mutex
15+
}
16+
17+
func newMemoryCache() Cache {
18+
return &memoryCache{
19+
cacheWorkers: env.Int("CACHE_TOTAL_WORKERS", 10),
20+
items: make(map[string]cacheItem),
21+
mut: sync.Mutex{},
22+
}
23+
}
24+
25+
// set ...
26+
func (m *memoryCache) set(key string, value V) {
27+
expiry := time.Hour * time.Duration(env.Int("MAX_CACHE_HOUR", 24))
28+
m.setWithExpiry(key, value, expiry)
29+
}
30+
31+
// setWithExpiry ...
32+
func (m *memoryCache) setWithExpiry(key string, value V, expiry time.Duration) {
33+
m.mut.Lock()
34+
defer m.mut.Unlock()
35+
item := cacheItem{
36+
value: value,
37+
createdAt: time.Now(),
38+
expiry: expiry,
39+
}
40+
m.items[key] = item
41+
}
42+
43+
// get ...
44+
func (m *memoryCache) get(key string) V {
45+
m.mut.Lock()
46+
defer m.mut.Unlock()
47+
48+
item, ok := m.items[key]
49+
if !ok {
50+
return nil
51+
}
52+
53+
return item.value
54+
}
55+
56+
// delete ...
57+
func (m *memoryCache) delete(key string) {
58+
m.mut.Lock()
59+
defer m.mut.Unlock()
60+
delete(m.items, key)
61+
}
62+
63+
// cleanUp deletes expired cache items and calls their callbacks
64+
func (m *memoryCache) cleanUp() {
65+
m.mut.Lock()
66+
defer m.mut.Unlock()
67+
for key, item := range m.items {
68+
if item.expired() {
69+
delete(m.items, key)
70+
}
71+
}
72+
}

cache/redis.go

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package cache
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/ochom/gutils/logs"
8+
"github.com/redis/go-redis/v9"
9+
)
10+
11+
// redisCache implements Cache
12+
type redisCache struct {
13+
client *redis.Client
14+
}
15+
16+
func newRedisCache(url ...string) Cache {
17+
if len(url) == 0 {
18+
logs.Error("newRedisCache: url is empty")
19+
return nil
20+
}
21+
22+
opt, err := redis.ParseURL(url[0])
23+
if err != nil {
24+
logs.Error("newRedisCache: %s", err.Error())
25+
return nil
26+
}
27+
28+
cl := redis.NewClient(opt)
29+
return &redisCache{
30+
client: cl,
31+
}
32+
}
33+
34+
// set ...
35+
func (r *redisCache) set(key string, value V) {
36+
r.setWithExpiry(key, value, 0)
37+
}
38+
39+
// setWithExpiry ...
40+
func (r *redisCache) setWithExpiry(key string, value V, expiry time.Duration) {
41+
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
42+
defer cancel()
43+
44+
if err := r.client.Set(ctx, key, value, expiry).Err(); err != nil {
45+
logs.Error("setWithCallback: %s", err.Error())
46+
}
47+
}
48+
49+
// get
50+
func (r *redisCache) get(key string) V {
51+
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
52+
defer cancel()
53+
54+
v, err := r.client.Get(ctx, key).Result()
55+
if err != nil {
56+
return nil
57+
}
58+
59+
return []byte(v)
60+
}
61+
62+
// delete ...
63+
func (r *redisCache) delete(key string) {
64+
if err := r.client.Del(context.Background(), key).Err(); err != nil {
65+
logs.Error("delete: %s", err.Error())
66+
}
67+
}
68+
69+
func (r *redisCache) cleanUp() {
70+
// TODO
71+
}

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/golang-jwt/jwt v3.2.2+incompatible
77
github.com/joho/godotenv v1.5.1
88
github.com/r3labs/sse/v2 v2.10.0
9+
github.com/redis/go-redis/v9 v9.5.3
910
github.com/streadway/amqp v1.0.0
1011
go.mongodb.org/mongo-driver v1.11.7
1112
golang.org/x/crypto v0.9.0
@@ -16,6 +17,8 @@ require (
1617
)
1718

1819
require (
20+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
21+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
1922
github.com/go-sql-driver/mysql v1.7.0 // indirect
2023
github.com/golang/snappy v0.0.1 // indirect
2124
github.com/jackc/pgpassfile v1.0.0 // indirect

0 commit comments

Comments
 (0)