start working on the frontend side
This commit is contained in:
143
backend/internal/common/cache/cache.go
vendored
Normal file
143
backend/internal/common/cache/cache.go
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CacheEntry represents a cached value with expiration
|
||||
type CacheEntry struct {
|
||||
Value interface{}
|
||||
ExpiresAt time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// IsExpired checks if the cache entry has expired
|
||||
func (e *CacheEntry) IsExpired() bool {
|
||||
return time.Now().After(e.ExpiresAt)
|
||||
}
|
||||
|
||||
// Cache provides an in-memory cache with TTL support
|
||||
type Cache struct {
|
||||
entries map[string]*CacheEntry
|
||||
mu sync.RWMutex
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// NewCache creates a new cache with a default TTL
|
||||
func NewCache(defaultTTL time.Duration) *Cache {
|
||||
c := &Cache{
|
||||
entries: make(map[string]*CacheEntry),
|
||||
ttl: defaultTTL,
|
||||
}
|
||||
|
||||
// Start background cleanup goroutine
|
||||
go c.cleanup()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Get retrieves a value from the cache
|
||||
func (c *Cache) Get(key string) (interface{}, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
entry, exists := c.entries[key]
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if entry.IsExpired() {
|
||||
// Don't delete here, let cleanup handle it
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return entry.Value, true
|
||||
}
|
||||
|
||||
// Set stores a value in the cache with the default TTL
|
||||
func (c *Cache) Set(key string, value interface{}) {
|
||||
c.SetWithTTL(key, value, c.ttl)
|
||||
}
|
||||
|
||||
// SetWithTTL stores a value in the cache with a custom TTL
|
||||
func (c *Cache) SetWithTTL(key string, value interface{}, ttl time.Duration) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.entries[key] = &CacheEntry{
|
||||
Value: value,
|
||||
ExpiresAt: time.Now().Add(ttl),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes a value from the cache
|
||||
func (c *Cache) Delete(key string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
delete(c.entries, key)
|
||||
}
|
||||
|
||||
// Clear removes all entries from the cache
|
||||
func (c *Cache) Clear() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.entries = make(map[string]*CacheEntry)
|
||||
}
|
||||
|
||||
// cleanup periodically removes expired entries
|
||||
func (c *Cache) cleanup() {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
c.mu.Lock()
|
||||
for key, entry := range c.entries {
|
||||
if entry.IsExpired() {
|
||||
delete(c.entries, key)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Stats returns cache statistics
|
||||
func (c *Cache) Stats() map[string]interface{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
total := len(c.entries)
|
||||
expired := 0
|
||||
for _, entry := range c.entries {
|
||||
if entry.IsExpired() {
|
||||
expired++
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_entries": total,
|
||||
"active_entries": total - expired,
|
||||
"expired_entries": expired,
|
||||
"default_ttl_seconds": int(c.ttl.Seconds()),
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateKey generates a cache key from a string
|
||||
func GenerateKey(prefix string, parts ...string) string {
|
||||
key := prefix
|
||||
for _, part := range parts {
|
||||
key += ":" + part
|
||||
}
|
||||
|
||||
// Hash long keys to keep them manageable
|
||||
if len(key) > 200 {
|
||||
hash := sha256.Sum256([]byte(key))
|
||||
return prefix + ":" + hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user