beta
This commit is contained in:
parent
a45ddf13d5
commit
bcd986e3f7
46 changed files with 6166 additions and 454 deletions
206
cache/cache.go
vendored
Normal file
206
cache/cache.go
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Item represents a cache item
|
||||
type Item struct {
|
||||
Value []byte
|
||||
Expiration int64
|
||||
}
|
||||
|
||||
// Expired returns true if the item has expired
|
||||
func (item Item) Expired() bool {
|
||||
if item.Expiration == 0 {
|
||||
return false
|
||||
}
|
||||
return time.Now().UnixNano() > item.Expiration
|
||||
}
|
||||
|
||||
// Cache represents an in-memory cache
|
||||
type Cache struct {
|
||||
items map[string]Item
|
||||
mu sync.RWMutex
|
||||
defaultExpiration time.Duration
|
||||
cleanupInterval time.Duration
|
||||
stopCleanup chan bool
|
||||
}
|
||||
|
||||
// New creates a new cache with the given default expiration and cleanup interval
|
||||
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
|
||||
cache := &Cache{
|
||||
items: make(map[string]Item),
|
||||
defaultExpiration: defaultExpiration,
|
||||
cleanupInterval: cleanupInterval,
|
||||
stopCleanup: make(chan bool),
|
||||
}
|
||||
|
||||
// Start cleanup goroutine if cleanup interval > 0
|
||||
if cleanupInterval > 0 {
|
||||
go cache.startCleanup()
|
||||
}
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
// startCleanup starts the cleanup process
|
||||
func (c *Cache) startCleanup() {
|
||||
ticker := time.NewTicker(c.cleanupInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.DeleteExpired()
|
||||
case <-c.stopCleanup:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set adds an item to the cache with the given key and expiration
|
||||
func (c *Cache) Set(key string, value interface{}, expiration time.Duration) error {
|
||||
// Convert value to bytes
|
||||
var valueBytes []byte
|
||||
var err error
|
||||
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
valueBytes = v
|
||||
case string:
|
||||
valueBytes = []byte(v)
|
||||
default:
|
||||
valueBytes, err = json.Marshal(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal value: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate expiration
|
||||
var exp int64
|
||||
if expiration == 0 {
|
||||
if c.defaultExpiration > 0 {
|
||||
exp = time.Now().Add(c.defaultExpiration).UnixNano()
|
||||
}
|
||||
} else if expiration > 0 {
|
||||
exp = time.Now().Add(expiration).UnixNano()
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.items[key] = Item{
|
||||
Value: valueBytes,
|
||||
Expiration: exp,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get gets an item from the cache
|
||||
func (c *Cache) Get(key string, value interface{}) (bool, error) {
|
||||
c.mu.RLock()
|
||||
item, found := c.items[key]
|
||||
c.mu.RUnlock()
|
||||
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if item has expired
|
||||
if item.Expired() {
|
||||
c.mu.Lock()
|
||||
delete(c.items, key)
|
||||
c.mu.Unlock()
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Unmarshal value
|
||||
switch v := value.(type) {
|
||||
case *[]byte:
|
||||
*v = item.Value
|
||||
case *string:
|
||||
*v = string(item.Value)
|
||||
default:
|
||||
if err := json.Unmarshal(item.Value, value); err != nil {
|
||||
return true, fmt.Errorf("failed to unmarshal value: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Delete deletes an item from the cache
|
||||
func (c *Cache) Delete(key string) {
|
||||
c.mu.Lock()
|
||||
delete(c.items, key)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// DeleteExpired deletes all expired items from the cache
|
||||
func (c *Cache) DeleteExpired() {
|
||||
now := time.Now().UnixNano()
|
||||
|
||||
c.mu.Lock()
|
||||
for k, v := range c.items {
|
||||
if v.Expiration > 0 && now > v.Expiration {
|
||||
delete(c.items, k)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// Clear deletes all items from the cache
|
||||
func (c *Cache) Clear() {
|
||||
c.mu.Lock()
|
||||
c.items = make(map[string]Item)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// Close stops the cleanup goroutine
|
||||
func (c *Cache) Close() {
|
||||
if c.cleanupInterval > 0 {
|
||||
c.stopCleanup <- true
|
||||
}
|
||||
}
|
||||
|
||||
// CacheService provides caching functionality
|
||||
type CacheService struct {
|
||||
cache *Cache
|
||||
}
|
||||
|
||||
// NewCacheService creates a new CacheService
|
||||
func NewCacheService(defaultExpiration, cleanupInterval time.Duration) *CacheService {
|
||||
return &CacheService{
|
||||
cache: New(defaultExpiration, cleanupInterval),
|
||||
}
|
||||
}
|
||||
|
||||
// Set adds an item to the cache
|
||||
func (s *CacheService) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
return s.cache.Set(key, value, expiration)
|
||||
}
|
||||
|
||||
// Get gets an item from the cache
|
||||
func (s *CacheService) Get(ctx context.Context, key string, value interface{}) (bool, error) {
|
||||
return s.cache.Get(key, value)
|
||||
}
|
||||
|
||||
// Delete deletes an item from the cache
|
||||
func (s *CacheService) Delete(ctx context.Context, key string) {
|
||||
s.cache.Delete(key)
|
||||
}
|
||||
|
||||
// Clear deletes all items from the cache
|
||||
func (s *CacheService) Clear(ctx context.Context) {
|
||||
s.cache.Clear()
|
||||
}
|
||||
|
||||
// Close closes the cache
|
||||
func (s *CacheService) Close() {
|
||||
s.cache.Close()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue