-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbase_cache.go
131 lines (110 loc) · 2.81 KB
/
base_cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package gormoize
import (
"sync"
"time"
"gorm.io/gorm"
)
type dbCacheEntry struct {
db *gorm.DB
lastUsed time.Time
}
// baseCache provides common caching functionality with cleanup support
type baseCache struct {
cacheMutex sync.RWMutex
dbCache map[string]*dbCacheEntry
cleanupInterval time.Duration
maxAge time.Duration
mockDB *gorm.DB
stopCleanup chan struct{}
}
// newBaseCache creates a new baseCache instance with the given options
func newBaseCache(opts Options) *baseCache {
cache := &baseCache{
cacheMutex: sync.RWMutex{},
dbCache: make(map[string]*dbCacheEntry),
cleanupInterval: opts.CleanupInterval,
maxAge: opts.MaxAge,
stopCleanup: make(chan struct{}),
}
if opts.MaxAge > 0 {
go cache.startCleanup()
}
return cache
}
// lastUsed returns a map of key to last used time for all cached items
func (c *baseCache) lastUsed() map[string]time.Time {
lastUsed := make(map[string]time.Time)
for key, entry := range c.dbCache {
lastUsed[key] = entry.lastUsed
}
return lastUsed
}
// cleanupItem removes the specified item from the cache and performs any necessary cleanup
func (c *baseCache) cleanupItem(key string) {
entry := c.dbCache[key]
// remove the specified item from the cache
// if key is nil or DNE, this is a no-op
delete(c.dbCache, key)
if entry != nil {
// close the database connection
sqlDB, err := entry.db.DB()
if err == nil {
sqlDB.Close()
}
}
}
// startCleanup starts the cleanup routine that removes old items
func (c *baseCache) startCleanup() {
// Run cleanup immediately once
c.cleanup()
ticker := time.NewTicker(c.cleanupInterval)
defer ticker.Stop()
// Start cleanup loop
for {
select {
case <-ticker.C:
c.cleanup()
case <-c.stopCleanup:
return
}
}
}
// mockDB returns the mock DB used for testing
func (c *baseCache) SetMockDB(db *gorm.DB) {
c.cacheMutex.Lock()
defer c.cacheMutex.Unlock()
c.mockDB = db
}
// Stop stops the cleanup routine and closes all database connections
func (c *baseCache) Stop() {
c.cacheMutex.Lock()
defer c.cacheMutex.Unlock()
// Clean up all database connections
for key := range c.dbCache {
c.cleanupItem(key)
}
if c.maxAge > 0 {
close(c.stopCleanup)
}
}
// Set adds or updates the cache entry for the provided key with the given *gorm.DB instance.
func (c *baseCache) Set(key string, db *gorm.DB) {
c.cacheMutex.Lock()
defer c.cacheMutex.Unlock()
// Add or update the cache entry with the current time
c.dbCache[key] = &dbCacheEntry{
db: db,
lastUsed: time.Now(),
}
}
// cleanup removes items that haven't been used for longer than maxAge
func (c *baseCache) cleanup() {
c.cacheMutex.Lock()
defer c.cacheMutex.Unlock()
now := time.Now()
for key, lastUsed := range c.lastUsed() {
if now.Sub(lastUsed) > c.maxAge {
c.cleanupItem(key)
}
}
}