A high-performance, thread-safe, generic in-memory cache library for Go with TTL support, automatic cleanup, and distributed invalidation capabilities.
- 🚀 High Performance: Shard-based architecture for concurrent access
- 🔧 Generic Types: Full support for Go generics (Go 1.21+)
- ⏰ TTL Support: Automatic expiration with configurable cleanup intervals
- 🔄 SingleFlight: Prevents duplicate requests for the same key
- 🌐 Distributed Invalidation: Optional cache invalidation with pluggable drivers (zero deps core)
- 🧵 Thread-Safe: Concurrent read/write operations with minimal locking
- 📊 Configurable: Customizable shard count, capacity, and sweep intervals
- 📝 Structured Logging: Built-in structured logging with configurable levels and formats
go get github.com/halilbulentorhon/invacache-goThis installs only the core cache functionality with no external dependencies.
InvaCache-Go uses a pluggable driver architecture to keep the core library dependency-free. Install only the drivers you need:
go get github.com/halilbulentorhon/invacache-go/invalidation/drivers/couchbasego get github.com/halilbulentorhon/invacache-go/invalidation/drivers/redis| Usage Scenario | Dependencies |
|---|---|
| In-Memory Only | ✅ Zero dependencies |
| + Couchbase | ✅ Only Couchbase SDK & cb-pubsub |
| + Redis | ✅ Only Redis client |
| + Both Drivers | ✅ Both driver dependencies |
Each driver is a separate module, so you only pull in what you actually use.
package main
import (
"fmt"
"time"
"github.com/halilbulentorhon/invacache-go"
"github.com/halilbulentorhon/invacache-go/config"
"github.com/halilbulentorhon/invacache-go/backend/option"
)
func main() {
// Create cache configuration
cfg := config.InvaCacheConfig{
BackendName: "in-memory",
Backend: &config.BackendConfig{
InMemory: &config.InMemoryConfig{
ShardCount: 16, // Number of shards for concurrent access
Capacity: 10000, // Maximum number of items
SweeperInterval: 30 * time.Second, // Cleanup interval
Ttl: "10m", // Default TTL for all items (optional)
},
},
}
// Create a new cache instance for string values
cache, err := invacache.NewCache[string](cfg)
if err != nil {
panic(err)
}
defer cache.Close() // Always cleanup resources
// Set a value with TTL
err = cache.Set("user:123", "John Doe", option.WithTTL(5*time.Minute))
if err != nil {
panic(err)
}
// Get a value
value, err := cache.Get("user:123")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("User: %s\n", value)
// Get or load pattern
user, err := cache.GetOrLoad("user:456", func(key string) (string, time.Duration, error) {
// This function is called only if the key is not in cache
// Simulate database lookup
return "Jane Doe", 10 * time.Minute, nil
})
if err != nil {
panic(err)
}
fmt.Printf("Loaded user: %s\n", user)
}type Cache[V any] interface {
Get(key string) (V, error)
GetOrLoad(key string, loader LoaderFunc[V]) (V, error)
Set(key string, value V, options ...option.OptFnc) error
Delete(key string, options ...option.DelOptFnc) error
Clear(options ...option.ClrOptFnc) error
Close() error
}type InvaCacheConfig struct {
BackendName string `json:"backendName"`
Backend *BackendConfig `json:"backend"`
Invalidation *InvalidationConfig `json:"invalidation,omitempty"`
}
type BackendConfig struct {
InMemory *InMemoryConfig `json:"inMemory"`
}
type InMemoryConfig struct {
ShardCount int `json:"shardCount"` // Default: 8
SweeperInterval time.Duration `json:"sweeperInterval"` // Default: 10 minutes
Capacity int `json:"capacity"` // Default: 1000
Ttl string `json:"ttl"` // Default TTL for all items (e.g., "10m", "1h")
}
### Options
**Set Options:**
- `option.WithTTL(duration)` - Set expiration time for specific key
- `option.WithNoExpiration()` - Set item to never expire
- `option.WithInvalidation()` - Trigger distributed invalidation on Set
**Delete Options:**
- `option.WithDeleteInvalidation()` - Trigger distributed invalidation on Delete
**Clear Options:**
- `option.WithClearInvalidation()` - Trigger distributed invalidation on Clear
## Advanced Usage
### Custom Types
```go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
cache, err := invacache.NewCache[User](cfg)
if err != nil {
panic(err)
}
defer cache.Close() // Always cleanup resources
user := User{ID: 1, Name: "John"}
cache.Set("user:1", user, option.WithTTL(time.Hour))
// Clear all cache
err = cache.Clear()
if err != nil {
panic(err)
}
// Clear with distributed invalidation
err = cache.Clear(option.WithClearInvalidation())
if err != nil {
panic(err)
}InvaCache-Go includes built-in structured logging to help you monitor cache operations and troubleshoot issues.
cache, err := invacache.NewCache[string](cfg)
if err != nil {
panic(err)
}// Note: NewCache function handles backend selection based on BackendName
// Use NewCache with custom logger configuration for development
cache, err := invacache.NewCache[string](cfg)
if err != nil {
panic(err)
}- debug: Detailed operation logs (cache hits/misses, individual operations)
- info: General information (cache initialization, invalidation events)
- warn: Warning messages (invalidation failures)
- error: Error conditions (cache operation failures)
{"time":"2025-09-06T13:07:07.846723+03:00","level":"INFO","msg":"initializing inmemory cache","component":"inmemory-cache","shard_count":16,"capacity":10000}
{"time":"2025-09-06T13:07:07.847048+03:00","level":"INFO","msg":"invalidation initialized successfully","component":"inmemory-cache","type":"couchbase"}time=2025-09-06T13:11:32.172+03:00 level=DEBUG msg="cache set operation" component=inmemory-cache key=user:123
time=2025-09-06T13:11:32.172+03:00 level=DEBUG msg="cache hit" component=inmemory-cache key=user:123
time=2025-09-06T13:11:32.172+03:00 level=DEBUG msg="cache delete successful" component=inmemory-cache key=user:123
InvaCache-Go supports pluggable invalidation drivers to keep your core library dependency-free. Each driver is a separate module that you install only when needed.
Available Drivers:
Install the Couchbase invalidation driver separately:
go get github.com/halilbulentorhon/invacache-go/invalidation/drivers/couchbaseThen use it in your application:
import (
"github.com/halilbulentorhon/invacache-go"
"github.com/halilbulentorhon/invacache-go/config"
// Import the Couchbase invalidation driver
_ "github.com/halilbulentorhon/invacache-go/invalidation/drivers/couchbase"
)
cfg := config.InvaCacheConfig{
BackendName: "in-memory",
Backend: &config.BackendConfig{
InMemory: &config.InMemoryConfig{
ShardCount: 16,
Capacity: 10000,
},
},
Invalidation: &config.InvalidationConfig{
Type: "couchbase",
DriverConfig: map[string]any{
"ConnectionString": "localhost:8091",
"Username": "admin",
"Password": "password",
"BucketName": "default",
"CollectionName": "_default",
"ScopeName": "_default",
"GroupName": "my-cache-group",
},
},
}
cache, err := invacache.NewCache[string](cfg)
if err != nil {
panic(err)
}
// Important: Always close the cache to properly cleanup resources
defer cache.Close() // This will stop invalidation goroutines and cleanup connectionsThe Couchbase driver uses the cb-pubsub library for reliable distributed messaging.
Important Notes:
- Invalidation subscription runs in a separate goroutine (non-blocking)
- Always call
cache.Close()to properly cleanup resources and stop background goroutines - Resilient Operation: Cache operations never fail due to invalidation issues
- Driver Responsibility: Retry logic and reconnection are handled by the invalidation driver (cb-pubsub)
- Graceful Degradation: Cache works locally when invalidation is unavailable
Install the Redis invalidation driver separately:
go get github.com/halilbulentorhon/invacache-go/invalidation/drivers/redisThen use it in your application:
import (
"time"
"github.com/halilbulentorhon/invacache-go"
"github.com/halilbulentorhon/invacache-go/config"
// Import the Redis invalidation driver
_ "github.com/halilbulentorhon/invacache-go/invalidation/drivers/redis"
)
cfg := config.InvaCacheConfig{
BackendName: "in-memory",
Backend: &config.BackendConfig{
InMemory: &config.InMemoryConfig{
ShardCount: 16,
Capacity: 10000,
},
},
Invalidation: &config.InvalidationConfig{
Type: "redis",
DriverConfig: map[string]any{
"Address": "localhost:6379",
"Password": "",
"DB": 0,
"Channel": "invacache:invalidation",
"PoolSize": 10,
"MaxRetries": 3,
"DialTimeout": 5 * time.Second,
},
},
}
cache, err := invacache.NewCache[string](cfg)
if err != nil {
panic(err)
}
defer cache.Close() // Always cleanup resourcesThe Redis driver uses Redis's native Pub/Sub mechanism for real-time distributed messaging.
Redis Driver Benefits:
- High Performance: Redis's optimized pub/sub implementation
- Real-time: Immediate invalidation propagation
- Scalable: Supports unlimited cache instances
- Simple Setup: Minimal Redis configuration required
- Reliable: Built on Redis's proven infrastructure
InvaCache-Go is designed for high-performance scenarios:
- Sharded Architecture: Reduces lock contention by distributing keys across multiple shards
- Minimal Allocations: Efficient memory usage with pre-allocated structures
- SingleFlight: Prevents thundering herd problems for expensive operations
- Concurrent Sweeping: Background cleanup doesn't block cache operations
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.