mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 11:02:54 +00:00
- Add CacheItem struct with key/value/expiration/createdAt/accessCount/lastAccess fields and IsExpired method - Add Cache interface with Get/Set/Delete/Clear/Keys/Size/Stats methods - Add CacheStats struct with hits/misses/hitRate/size/evictions/totalItems metrics - Add MemoryCache with sync.RWMutex, maxSize limit, automatic cleanup goroutine, LRU eviction, and access tracking - Add RedisCache placeholder
413 lines
8.9 KiB
Go
413 lines
8.9 KiB
Go
package cache
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// CacheItem represents an item in the cache
|
|
type CacheItem struct {
|
|
Key string `json:"key"`
|
|
Value any `json:"value"`
|
|
Expiration time.Time `json:"expiration"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
AccessCount int64 `json:"access_count"`
|
|
LastAccess time.Time `json:"last_access"`
|
|
}
|
|
|
|
// IsExpired checks if the cache item has expired
|
|
func (ci *CacheItem) IsExpired() bool {
|
|
return time.Now().After(ci.Expiration)
|
|
}
|
|
|
|
// Cache interface defines the cache operations
|
|
type Cache interface {
|
|
Get(key string) (any, bool)
|
|
Set(key string, value any, ttl time.Duration)
|
|
Delete(key string)
|
|
Clear()
|
|
Keys() []string
|
|
Size() int
|
|
Stats() CacheStats
|
|
}
|
|
|
|
// CacheStats provides cache statistics
|
|
type CacheStats struct {
|
|
Hits int64 `json:"hits"`
|
|
Misses int64 `json:"misses"`
|
|
HitRate float64 `json:"hit_rate"`
|
|
Size int `json:"size"`
|
|
Evictions int64 `json:"evictions"`
|
|
TotalItems int64 `json:"total_items"`
|
|
}
|
|
|
|
// MemoryCache is an in-memory cache implementation
|
|
type MemoryCache struct {
|
|
items map[string]*CacheItem
|
|
mu sync.RWMutex
|
|
stats CacheStats
|
|
maxSize int
|
|
cleanupTick *time.Ticker
|
|
stopCleanup chan bool
|
|
}
|
|
|
|
// NewMemoryCache creates a new memory cache
|
|
func NewMemoryCache(maxSize int, cleanupInterval time.Duration) *MemoryCache {
|
|
cache := &MemoryCache{
|
|
items: make(map[string]*CacheItem),
|
|
maxSize: maxSize,
|
|
cleanupTick: time.NewTicker(cleanupInterval),
|
|
stopCleanup: make(chan bool),
|
|
}
|
|
|
|
// Start cleanup goroutine
|
|
go cache.cleanup()
|
|
|
|
return cache
|
|
}
|
|
|
|
// cleanup removes expired items from cache
|
|
func (mc *MemoryCache) cleanup() {
|
|
for {
|
|
select {
|
|
case <-mc.cleanupTick.C:
|
|
mc.removeExpired()
|
|
case <-mc.stopCleanup:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// removeExpired removes expired items from cache
|
|
func (mc *MemoryCache) removeExpired() {
|
|
mc.mu.Lock()
|
|
defer mc.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
for key, item := range mc.items {
|
|
if now.After(item.Expiration) {
|
|
delete(mc.items, key)
|
|
mc.stats.Evictions++
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get retrieves an item from cache
|
|
func (mc *MemoryCache) Get(key string) (any, bool) {
|
|
mc.mu.RLock()
|
|
defer mc.mu.RUnlock()
|
|
|
|
item, exists := mc.items[key]
|
|
if !exists {
|
|
mc.stats.Misses++
|
|
return nil, false
|
|
}
|
|
|
|
if item.IsExpired() {
|
|
delete(mc.items, key)
|
|
mc.stats.Misses++
|
|
mc.stats.Evictions++
|
|
return nil, false
|
|
}
|
|
|
|
// Update access statistics
|
|
item.AccessCount++
|
|
item.LastAccess = time.Now()
|
|
mc.stats.Hits++
|
|
|
|
return item.Value, true
|
|
}
|
|
|
|
// Set stores an item in cache
|
|
func (mc *MemoryCache) Set(key string, value any, ttl time.Duration) {
|
|
mc.mu.Lock()
|
|
defer mc.mu.Unlock()
|
|
|
|
// Check if we need to evict items to make space
|
|
if len(mc.items) >= mc.maxSize {
|
|
mc.evictLRU()
|
|
}
|
|
|
|
expiration := time.Now().Add(ttl)
|
|
item := &CacheItem{
|
|
Key: key,
|
|
Value: value,
|
|
Expiration: expiration,
|
|
CreatedAt: time.Now(),
|
|
AccessCount: 1,
|
|
LastAccess: time.Now(),
|
|
}
|
|
|
|
mc.items[key] = item
|
|
mc.stats.TotalItems++
|
|
}
|
|
|
|
// evictLRU removes the least recently used item
|
|
func (mc *MemoryCache) evictLRU() {
|
|
var lruKey string
|
|
var lruTime time.Time
|
|
|
|
for key, item := range mc.items {
|
|
if lruKey == "" || item.LastAccess.Before(lruTime) {
|
|
lruKey = key
|
|
lruTime = item.LastAccess
|
|
}
|
|
}
|
|
|
|
if lruKey != "" {
|
|
delete(mc.items, lruKey)
|
|
mc.stats.Evictions++
|
|
}
|
|
}
|
|
|
|
// Delete removes an item from cache
|
|
func (mc *MemoryCache) Delete(key string) {
|
|
mc.mu.Lock()
|
|
defer mc.mu.Unlock()
|
|
|
|
delete(mc.items, key)
|
|
}
|
|
|
|
// Clear removes all items from cache
|
|
func (mc *MemoryCache) Clear() {
|
|
mc.mu.Lock()
|
|
defer mc.mu.Unlock()
|
|
|
|
mc.items = make(map[string]*CacheItem)
|
|
mc.stats.Evictions += int64(len(mc.items))
|
|
}
|
|
|
|
// Keys returns all keys in cache
|
|
func (mc *MemoryCache) Keys() []string {
|
|
mc.mu.RLock()
|
|
defer mc.mu.RUnlock()
|
|
|
|
keys := make([]string, 0, len(mc.items))
|
|
for key := range mc.items {
|
|
keys = append(keys, key)
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// Size returns the current cache size
|
|
func (mc *MemoryCache) Size() int {
|
|
mc.mu.RLock()
|
|
defer mc.mu.RUnlock()
|
|
|
|
return len(mc.items)
|
|
}
|
|
|
|
// Stats returns cache statistics
|
|
func (mc *MemoryCache) Stats() CacheStats {
|
|
mc.mu.RLock()
|
|
defer mc.mu.RUnlock()
|
|
|
|
total := mc.stats.Hits + mc.stats.Misses
|
|
hitRate := 0.0
|
|
if total > 0 {
|
|
hitRate = float64(mc.stats.Hits) / float64(total) * 100
|
|
}
|
|
|
|
return CacheStats{
|
|
Hits: mc.stats.Hits,
|
|
Misses: mc.stats.Misses,
|
|
HitRate: hitRate,
|
|
Size: len(mc.items),
|
|
Evictions: mc.stats.Evictions,
|
|
TotalItems: mc.stats.TotalItems,
|
|
}
|
|
}
|
|
|
|
// Close stops the cache cleanup goroutine
|
|
func (mc *MemoryCache) Close() {
|
|
mc.cleanupTick.Stop()
|
|
close(mc.stopCleanup)
|
|
}
|
|
|
|
// RedisCache is a Redis-based cache implementation (placeholder)
|
|
type RedisCache struct {
|
|
// Redis client would be here
|
|
}
|
|
|
|
// NewRedisCache creates a new Redis cache
|
|
func NewRedisCache(addr string, password string, db int) *RedisCache {
|
|
// Implementation would connect to Redis
|
|
return &RedisCache{}
|
|
}
|
|
|
|
// Get retrieves an item from Redis cache
|
|
func (rc *RedisCache) Get(key string) (any, bool) {
|
|
// Redis GET implementation
|
|
return nil, false
|
|
}
|
|
|
|
// Set stores an item in Redis cache
|
|
func (rc *RedisCache) Set(key string, value any, ttl time.Duration) {
|
|
// Redis SET implementation
|
|
}
|
|
|
|
// Delete removes an item from Redis cache
|
|
func (rc *RedisCache) Delete(key string) {
|
|
// Redis DEL implementation
|
|
}
|
|
|
|
// Clear removes all items from Redis cache
|
|
func (rc *RedisCache) Clear() {
|
|
// Redis FLUSH implementation
|
|
}
|
|
|
|
// Keys returns all keys in Redis cache
|
|
func (rc *RedisCache) Keys() []string {
|
|
// Redis KEYS implementation
|
|
return nil
|
|
}
|
|
|
|
// Size returns the current cache size
|
|
func (rc *RedisCache) Size() int {
|
|
// Redis DBSIZE implementation
|
|
return 0
|
|
}
|
|
|
|
// Stats returns cache statistics
|
|
func (rc *RedisCache) Stats() CacheStats {
|
|
// Redis INFO implementation
|
|
return CacheStats{}
|
|
}
|
|
|
|
// CacheManager manages multiple cache instances
|
|
type CacheManager struct {
|
|
caches map[string]Cache
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewCacheManager creates a new cache manager
|
|
func NewCacheManager() *CacheManager {
|
|
return &CacheManager{
|
|
caches: make(map[string]Cache),
|
|
}
|
|
}
|
|
|
|
// AddCache adds a cache instance
|
|
func (cm *CacheManager) AddCache(name string, cache Cache) {
|
|
cm.mu.Lock()
|
|
defer cm.mu.Unlock()
|
|
cm.caches[name] = cache
|
|
}
|
|
|
|
// GetCache retrieves a cache instance
|
|
func (cm *CacheManager) GetCache(name string) (Cache, bool) {
|
|
cm.mu.RLock()
|
|
defer cm.mu.RUnlock()
|
|
cache, exists := cm.caches[name]
|
|
return cache, exists
|
|
}
|
|
|
|
// RemoveCache removes a cache instance
|
|
func (cm *CacheManager) RemoveCache(name string) {
|
|
cm.mu.Lock()
|
|
defer cm.mu.Unlock()
|
|
delete(cm.caches, name)
|
|
}
|
|
|
|
// GetAllStats returns statistics for all caches
|
|
func (cm *CacheManager) GetAllStats() map[string]CacheStats {
|
|
cm.mu.RLock()
|
|
defer cm.mu.RUnlock()
|
|
|
|
stats := make(map[string]CacheStats)
|
|
for name, cache := range cm.caches {
|
|
stats[name] = cache.Stats()
|
|
}
|
|
return stats
|
|
}
|
|
|
|
// ClearAll clears all caches
|
|
func (cm *CacheManager) ClearAll() {
|
|
cm.mu.RLock()
|
|
defer cm.mu.RUnlock()
|
|
|
|
for _, cache := range cm.caches {
|
|
cache.Clear()
|
|
}
|
|
}
|
|
|
|
// Global cache manager instance
|
|
var globalCacheManager *CacheManager
|
|
|
|
// InitializeCacheManager initializes the global cache manager
|
|
func InitializeCacheManager() {
|
|
globalCacheManager = NewCacheManager()
|
|
|
|
// Add default memory cache
|
|
memoryCache := NewMemoryCache(1000, 5*time.Minute)
|
|
globalCacheManager.AddCache("default", memoryCache)
|
|
|
|
// Add specialized caches
|
|
userCache := NewMemoryCache(500, 10*time.Minute)
|
|
globalCacheManager.AddCache("users", userCache)
|
|
|
|
serviceCache := NewMemoryCache(200, 2*time.Minute)
|
|
globalCacheManager.AddCache("services", serviceCache)
|
|
|
|
metricsCache := NewMemoryCache(1000, 1*time.Minute)
|
|
globalCacheManager.AddCache("metrics", metricsCache)
|
|
}
|
|
|
|
// GetCacheManager returns the global cache manager
|
|
func GetCacheManager() *CacheManager {
|
|
return globalCacheManager
|
|
}
|
|
|
|
// GetDefaultCache returns the default cache
|
|
func GetDefaultCache() Cache {
|
|
if globalCacheManager == nil {
|
|
InitializeCacheManager()
|
|
}
|
|
cache, _ := globalCacheManager.GetCache("default")
|
|
return cache
|
|
}
|
|
|
|
// GetUserCache returns the user cache
|
|
func GetUserCache() Cache {
|
|
if globalCacheManager == nil {
|
|
InitializeCacheManager()
|
|
}
|
|
cache, _ := globalCacheManager.GetCache("users")
|
|
return cache
|
|
}
|
|
|
|
// GetServiceCache returns the service cache
|
|
func GetServiceCache() Cache {
|
|
if globalCacheManager == nil {
|
|
InitializeCacheManager()
|
|
}
|
|
cache, _ := globalCacheManager.GetCache("services")
|
|
return cache
|
|
}
|
|
|
|
// GetMetricsCache returns the metrics cache
|
|
func GetMetricsCache() Cache {
|
|
if globalCacheManager == nil {
|
|
InitializeCacheManager()
|
|
}
|
|
cache, _ := globalCacheManager.GetCache("metrics")
|
|
return cache
|
|
}
|
|
|
|
// CacheKey generates a cache key with namespace
|
|
func CacheKey(namespace, key string) string {
|
|
return fmt.Sprintf("%s:%s", namespace, key)
|
|
}
|
|
|
|
// SerializeValue serializes a value for caching
|
|
func SerializeValue(value any) ([]byte, error) {
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
// DeserializeValue deserializes a value from cache
|
|
func DeserializeValue(data []byte, target any) error {
|
|
return json.Unmarshal(data, target)
|
|
}
|