In-memory cache store implementation with LRU eviction for Forge AI SDK.
- β Pure Go implementation (no external dependencies)
- β LRU (Least Recently Used) eviction policy
- β TTL (Time To Live) support with automatic cleanup
- β Thread-safe with RWMutex
- β Size limits with automatic eviction
- β Zero configuration required
- β Observability (logging & metrics)
- β Perfect for testing and local development
go get github.com/xraph/ai-sdk/integrations/caches/memorypackage main
import (
"context"
"log"
"github.com/xraph/ai-sdk/integrations/caches/memory"
)
func main() {
ctx := context.Background()
// Create memory cache store
cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 1000, // Maximum 1000 entries
})
defer cache.Close()
// Set value
err := cache.Set(ctx, "user:123", []byte(`{"name":"John"}`), 0)
if err != nil {
log.Fatal(err)
}
// Get value
value, found, err := cache.Get(ctx, "user:123")
if err != nil {
log.Fatal(err)
}
if found {
log.Printf("Found: %s\n", string(value))
}
// Delete value
err = cache.Delete(ctx, "user:123")
if err != nil {
log.Fatal(err)
}
}import "time"
// Cache with 5-minute TTL
cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 1000,
})
// Set with expiration
err := cache.Set(ctx, "session:abc", []byte("data"), 5*time.Minute)
// After 5 minutes, the entry will be automatically removed// Small cache to demonstrate LRU
cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 3, // Only 3 entries max
})
// Add 4 entries - oldest will be evicted
cache.Set(ctx, "key1", []byte("val1"), 0)
cache.Set(ctx, "key2", []byte("val2"), 0)
cache.Set(ctx, "key3", []byte("val3"), 0)
cache.Set(ctx, "key4", []byte("val4"), 0) // key1 evicted
// key1 no longer exists
_, found, _ := cache.Get(ctx, "key1") // found = falsefunc TestMyFeature(t *testing.T) {
cache := memory.NewMemoryCacheStore(memory.Config{})
defer cache.Close()
// Use cache in tests...
// Clear all entries
cache.Clear(context.Background())
// Check size
if cache.Size() != 0 {
t.Errorf("expected empty cache, got %d entries", cache.Size())
}
}type Config struct {
MaxSize int // Maximum number of entries (0 = unlimited)
Logger logger.Logger // Optional: Logger for debugging
Metrics metrics.Metrics // Optional: Metrics for monitoring
}Sets a value in the cache with optional TTL.
func (m *MemoryCacheStore) Set(ctx context.Context, key string, value []byte, ttl time.Duration) errorRetrieves a value from the cache.
func (m *MemoryCacheStore) Get(ctx context.Context, key string) ([]byte, bool, error)Returns: value, found, error
Removes a value from the cache.
func (m *MemoryCacheStore) Delete(ctx context.Context, key string) errorClears all entries from the cache.
func (m *MemoryCacheStore) Clear(ctx context.Context) errorReturns the current number of entries.
func (m *MemoryCacheStore) Size() intStops the cleanup goroutine and releases resources.
func (m *MemoryCacheStore) Close() errorgo test ./...
go test -race ./... # Test for race conditions
go test -bench=. ./... # Run benchmarks| Operation | Latency | Notes |
|---|---|---|
| Set | ~2ΞΌs | O(1) with occasional O(n) on eviction |
| Get | ~1ΞΌs | O(1) lookup |
| Delete | ~1ΞΌs | O(1) removal |
| Clear | ~10ΞΌs | O(n) |
Benchmarks on MacBook Pro M1, 1000 entries in cache
cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 10000, // 10k entries max
})cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 0, // Unlimited (be careful with memory!)
})import (
"github.com/xraph/go-utils/log"
"github.com/xraph/go-utils/metrics"
)
cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 1000,
Logger: log.NewLogger(),
Metrics: metrics.NewMetrics(),
})- β Extremely fast (in-memory)
- β No external dependencies
- β Perfect for testing
- β LRU eviction prevents unbounded growth
- β TTL support for auto-expiration
- β Data lost on restart (no persistence)
- β No replication or high availability
- β Limited to single instance memory
- β No cross-process sharing
- β Unit and integration tests
- β Local development
- β Single-instance applications
- β Session caching
- β Rate limiting
- β Temporary data storage
- Redis CacheStore - Distributed, persistent
- External cache services (Memcached, etc.)
cache := memory.NewMemoryCacheStore(memory.Config{
MaxSize: 10000,
})
// Store session with 30-minute TTL
sessionData := []byte(`{"user_id":123,"role":"admin"}`)
cache.Set(ctx, "session:"+sessionID, sessionData, 30*time.Minute)func checkRateLimit(userID string) bool {
key := fmt.Sprintf("ratelimit:%s", userID)
// Check current count
data, found, _ := cache.Get(ctx, key)
count := 0
if found {
json.Unmarshal(data, &count)
}
if count >= 100 {
return false // Rate limit exceeded
}
// Increment count
count++
data, _ = json.Marshal(count)
cache.Set(ctx, key, data, 1*time.Minute)
return true
}func getExpensiveData(id string) ([]byte, error) {
key := fmt.Sprintf("data:%s", id)
// Check cache first
if data, found, _ := cache.Get(ctx, key); found {
return data, nil
}
// Compute expensive result
data := computeExpensiveResult(id)
// Cache for 10 minutes
cache.Set(ctx, key, data, 10*time.Minute)
return data, nil
}Issue: Cache consuming too much memory
Solutions:
- Set appropriate
MaxSizelimit - Use shorter TTLs
- Clear cache periodically
- Monitor
Size()metric
Issue: Frequently accessed items being evicted
Solutions:
- Increase
MaxSize - Reduce TTL for less important items
- Use dedicated cache instances for different data types
Issue: Data race warnings
Solution: The cache is already thread-safe. If seeing issues, check if:
- Cache instance is being shared correctly
- Not storing pointers to mutable data
Contributions welcome! See the main CONTRIBUTING.md.
MIT License - see LICENSE