Skip to content

Commit 018bf8f

Browse files
committed
update
1 parent 6562a62 commit 018bf8f

File tree

11 files changed

+201
-1482
lines changed

11 files changed

+201
-1482
lines changed

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ module github.com/hhftechnology/traefik-queue-manager
22

33
go 1.19
44

5-
require github.com/patrickmn/go-cache v2.1.0+incompatible

go.sum

Lines changed: 0 additions & 2 deletions
This file was deleted.

internal_cache.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// internal_cache.go
2+
package queuemanager
3+
4+
import (
5+
"sync"
6+
"time"
7+
)
8+
9+
// Item represents a cached item with expiration
10+
type CacheItem struct {
11+
Object interface{}
12+
Expiration int64 // Unix nano time when this item expires
13+
}
14+
15+
// SimpleCache provides a thread-safe in-memory key/value store with expiration
16+
type SimpleCache struct {
17+
items map[string]CacheItem
18+
mu sync.RWMutex
19+
defaultExpiration time.Duration
20+
cleanupInterval time.Duration
21+
stopCleanup chan bool
22+
}
23+
24+
// Constants for expiration
25+
const (
26+
NoExpiration time.Duration = -1
27+
DefaultExpiration time.Duration = 0
28+
)
29+
30+
// NewSimpleCache creates a new cache with the provided default expiration duration
31+
// and cleanup interval. If the cleanup interval is <= 0, expired items are not
32+
// automatically deleted from the cache.
33+
func NewSimpleCache(defaultExpiration, cleanupInterval time.Duration) *SimpleCache {
34+
cache := &SimpleCache{
35+
items: make(map[string]CacheItem),
36+
defaultExpiration: defaultExpiration,
37+
cleanupInterval: cleanupInterval,
38+
}
39+
40+
// Start the cleanup goroutine if a cleanup interval is specified
41+
if cleanupInterval > 0 {
42+
cache.stopCleanup = make(chan bool)
43+
go cache.startCleanupTimer()
44+
}
45+
46+
return cache
47+
}
48+
49+
// startCleanupTimer starts a timer that periodically cleans up expired items
50+
func (c *SimpleCache) startCleanupTimer() {
51+
ticker := time.NewTicker(c.cleanupInterval)
52+
defer ticker.Stop()
53+
for {
54+
select {
55+
case <-ticker.C:
56+
c.DeleteExpired()
57+
case <-c.stopCleanup:
58+
return
59+
}
60+
}
61+
}
62+
63+
// Set adds an item to the cache, replacing any existing item with the same key
64+
func (c *SimpleCache) Set(k string, x interface{}, d time.Duration) {
65+
var exp int64
66+
67+
if d == DefaultExpiration {
68+
d = c.defaultExpiration
69+
}
70+
71+
if d > 0 {
72+
exp = time.Now().Add(d).UnixNano()
73+
}
74+
75+
c.mu.Lock()
76+
c.items[k] = CacheItem{
77+
Object: x,
78+
Expiration: exp,
79+
}
80+
c.mu.Unlock()
81+
}
82+
83+
// Get retrieves an item from the cache and returns (item, found)
84+
func (c *SimpleCache) Get(k string) (interface{}, bool) {
85+
c.mu.RLock()
86+
item, found := c.items[k]
87+
if !found {
88+
c.mu.RUnlock()
89+
return nil, false
90+
}
91+
92+
// Check if the item has expired
93+
if item.Expiration > 0 {
94+
if time.Now().UnixNano() > item.Expiration {
95+
c.mu.RUnlock()
96+
return nil, false
97+
}
98+
}
99+
100+
c.mu.RUnlock()
101+
return item.Object, true
102+
}
103+
104+
// Delete removes an item from the cache
105+
func (c *SimpleCache) Delete(k string) {
106+
c.mu.Lock()
107+
delete(c.items, k)
108+
c.mu.Unlock()
109+
}
110+
111+
// DeleteExpired deletes all expired items from the cache
112+
func (c *SimpleCache) DeleteExpired() {
113+
now := time.Now().UnixNano()
114+
c.mu.Lock()
115+
defer c.mu.Unlock()
116+
117+
for k, v := range c.items {
118+
// Delete if the item has expired
119+
if v.Expiration > 0 && now > v.Expiration {
120+
delete(c.items, k)
121+
}
122+
}
123+
}
124+
125+
// Stop ends the cleanup timer if it's running
126+
func (c *SimpleCache) Stop() {
127+
if c.cleanupInterval > 0 {
128+
c.stopCleanup <- true
129+
}
130+
}
131+
132+
// ItemCount returns the number of items in the cache (including expired items)
133+
func (c *SimpleCache) ItemCount() int {
134+
c.mu.RLock()
135+
n := len(c.items)
136+
c.mu.RUnlock()
137+
return n
138+
}
139+
140+
// Flush deletes all items from the cache
141+
func (c *SimpleCache) Flush() {
142+
c.mu.Lock()
143+
c.items = make(map[string]CacheItem)
144+
c.mu.Unlock()
145+
}

queue-manager.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ import (
1818
"strconv"
1919
"strings"
2020
"time"
21-
22-
"github.com/patrickmn/go-cache"
2321
)
2422

2523
// Config holds the plugin configuration.
@@ -81,7 +79,7 @@ type QueueManager struct {
8179
next http.Handler
8280
name string
8381
config *Config
84-
cache *cache.Cache
82+
cache *SimpleCache
8583
template *template.Template
8684
queue []Session
8785
activeSessionIDs map[string]bool
@@ -114,7 +112,7 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
114112
next: next,
115113
name: name,
116114
config: config,
117-
cache: cache.New(config.SessionTime, config.PurgeTime),
115+
cache: NewSimpleCache(config.SessionTime, config.PurgeTime),
118116
template: tmpl,
119117
queue: make([]Session, 0),
120118
activeSessionIDs: make(map[string]bool),
@@ -169,7 +167,7 @@ func (qm *QueueManager) canClientProceed(clientID string) bool {
169167
}
170168

171169
qm.activeSessionIDs[clientID] = true
172-
qm.cache.Set(clientID, session, cache.DefaultExpiration)
170+
qm.cache.Set(clientID, session, DefaultExpiration)
173171
return true
174172
}
175173

@@ -179,8 +177,8 @@ func (qm *QueueManager) canClientProceed(clientID string) bool {
179177

180178
// updateClientTimestamp updates the last seen timestamp for a client.
181179
func (qm *QueueManager) updateClientTimestamp(clientID string) {
182-
if session, found := qm.cache.Get(clientID); found {
183-
sessionData, ok := session.(Session)
180+
if sessionObj, found := qm.cache.Get(clientID); found {
181+
sessionData, ok := sessionObj.(Session)
184182
if !ok {
185183
if qm.config.Debug {
186184
log.Printf("[Queue Manager] Error: Failed to convert session to Session type")
@@ -189,7 +187,7 @@ func (qm *QueueManager) updateClientTimestamp(clientID string) {
189187
}
190188

191189
sessionData.LastSeen = time.Now()
192-
qm.cache.Set(clientID, sessionData, cache.DefaultExpiration)
190+
qm.cache.Set(clientID, sessionData, DefaultExpiration)
193191
}
194192
}
195193

@@ -213,7 +211,7 @@ func (qm *QueueManager) placeClientInQueue(clientID string) int {
213211
Position: len(qm.queue),
214212
}
215213
qm.queue = append(qm.queue, session)
216-
qm.cache.Set(clientID, session, cache.DefaultExpiration)
214+
qm.cache.Set(clientID, session, DefaultExpiration)
217215
position = len(qm.queue) - 1
218216
}
219217

@@ -483,11 +481,11 @@ func (qm *QueueManager) CleanupExpiredSessions() {
483481

484482
// Update positions in queue
485483
for i := range qm.queue {
486-
if session, found := qm.cache.Get(qm.queue[i].ID); found {
487-
sessionData, ok := session.(Session)
484+
if sessionObj, found := qm.cache.Get(qm.queue[i].ID); found {
485+
sessionData, ok := sessionObj.(Session)
488486
if ok {
489487
sessionData.Position = i
490-
qm.cache.Set(qm.queue[i].ID, sessionData, cache.DefaultExpiration)
488+
qm.cache.Set(qm.queue[i].ID, sessionData, DefaultExpiration)
491489
}
492490
}
493491
}

queue-manager_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"os"
88
"testing"
9+
"time"
910
)
1011

1112
func TestGenerateUniqueID(t *testing.T) {
@@ -155,4 +156,49 @@ func TestFileExists(t *testing.T) {
155156
if fileExists("nonexistent_file.txt") {
156157
t.Errorf("fileExists should return false for a non-existent file")
157158
}
159+
}
160+
161+
// Add tests for the SimpleCache
162+
func TestSimpleCache(t *testing.T) {
163+
// Create a new cache with default expiration of 1 second
164+
cache := NewSimpleCache(time.Second, time.Second*2)
165+
166+
// Test Set and Get
167+
cache.Set("key1", "value1", DefaultExpiration)
168+
val, found := cache.Get("key1")
169+
if !found {
170+
t.Errorf("Expected to find key1 in cache")
171+
}
172+
if val != "value1" {
173+
t.Errorf("Expected value1, got %v", val)
174+
}
175+
176+
// Test with non-existant key
177+
_, found = cache.Get("nonexistent")
178+
if found {
179+
t.Errorf("Should not find nonexistent key")
180+
}
181+
182+
// Test expiration
183+
cache.Set("key2", "value2", 500*time.Millisecond)
184+
time.Sleep(800 * time.Millisecond)
185+
_, found = cache.Get("key2")
186+
if found {
187+
t.Errorf("key2 should have expired")
188+
}
189+
190+
// Test Delete
191+
cache.Set("key3", "value3", NoExpiration)
192+
cache.Delete("key3")
193+
_, found = cache.Get("key3")
194+
if found {
195+
t.Errorf("key3 should have been deleted")
196+
}
197+
198+
// Test Flush
199+
cache.Set("key4", "value4", NoExpiration)
200+
cache.Flush()
201+
if cache.ItemCount() != 0 {
202+
t.Errorf("Cache should be empty after Flush")
203+
}
158204
}

vendor/github.com/patrickmn/go-cache/CONTRIBUTORS

Lines changed: 0 additions & 9 deletions
This file was deleted.

vendor/github.com/patrickmn/go-cache/LICENSE

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)