A high-performance, generic similarity-based cache for Go with intelligent sharding and pluggable eviction policies.
Synapse is a thread-safe, context-aware cache that goes beyond traditional key-value storage by supporting similarity-based lookups. When an exact key match isn't found, Synapse can find the "closest" matching key based on a configurable similarity function and threshold.
- π― Similarity-Based Lookups: Find approximate matches when exact keys don't exist
- π§ Generic Types: Fully type-safe with Go generics (1.18+)
- β‘ High Performance: Automatic sharding distributes load across multiple concurrent-safe partitions
- π§© Pluggable Similarity Functions: Define custom similarity algorithms for your use case
- β»οΈ Eviction Policies: Currently supports LRU with more policies coming soon
- β° TTL Support: Automatic expiration of cache entries
- π·οΈ Namespace Isolation: Partition cache entries by namespace via context
- π Thread-Safe: Lock-free reads and efficient write locking per shard
- π Metadata Support: Attach custom metadata to cache entries
- π Context-Aware: Full context.Context integration for cancellation and values
go get github.com/kolosys/synapsepackage main
import (
"context"
"fmt"
"strings"
"github.com/kolosys/synapse"
"github.com/kolosys/synapse/eviction"
)
func stringSimilarity(a, b string) float64 {
a, b = strings.ToLower(a), strings.ToLower(b)
if a == b {
return 1.0
}
minLen := min(len(a), len(b))
matches := 0
for i := 0; i < minLen; i++ {
if a[i] == b[i] {
matches++
} else {
break
}
}
return float64(matches) / float64(max(len(a), len(b)))
}
func main() {
ctx := context.Background()
cache := synapse.New[string, string](
synapse.WithMaxSize(1000),
synapse.WithShards(16),
synapse.WithThreshold(0.7),
synapse.WithEviction(eviction.NewLRU(1000)),
)
cache.WithSimilarity(stringSimilarity)
cache.Set(ctx, "user:alice", "Alice's data")
cache.Set(ctx, "user:bob", "Bob's data")
if value, found := cache.Get(ctx, "user:alice"); found {
fmt.Println("Exact match:", value)
}
value, key, score, found := cache.GetSimilar(ctx, "user:ali")
if found {
fmt.Printf("Similar match: %s (key: %s, score: %.2f)\n", value, key, score)
}
}New[K, V](opts ...Option) *Cache[K, V]- Create a new cache instanceGet(ctx context.Context, key K) (V, bool)- Retrieve value by exact key matchSet(ctx context.Context, key K, value V) error- Store a key-value pairGetSimilar(ctx context.Context, key K) (V, K, float64, bool)- Find most similar key above thresholdDelete(ctx context.Context, key K) bool- Remove a key from the cacheLen() int- Get total number of entries across all shardsWithSimilarity(fn SimilarityFunc[K]) *Cache[K, V]- Set similarity function
| Option | Description | Default |
|---|---|---|
WithShards(n) |
Number of shards (1-256) | 16 |
WithMaxSize(size) |
Maximum number of entries | 1000 |
WithThreshold(t) |
Similarity threshold (0.0-1.0) | 0.8 |
WithEviction(policy) |
Eviction policy | nil |
WithTTL(duration) |
Time-to-live for entries | 0 (no expiration) |
WithStats(enable) |
Enable statistics tracking | false |
WithNamespace(ctx context.Context, namespace string) context.Context- Add namespace to contextGetNamespace(ctx context.Context) string- Retrieve namespace from contextWithMetadata(ctx context.Context, key string, value any) context.Context- Add metadata to contextGetMetadata(ctx context.Context, key string) (any, bool)- Retrieve metadata from context
ctx := context.Background()
cache := synapse.New[string, int]()
cache.Set(ctx, "key1", 42)
if value, found := cache.Get(ctx, "key1"); found {
fmt.Println("Value:", value)
}
cache.Delete(ctx, "key1")
size := cache.Len()cache := synapse.New[string, string]()
cache.WithSimilarity(func(a, b string) float64 {
// Return similarity score between 0.0 and 1.0
return computeSimilarity(a, b)
})
cache.Set(ctx, "apple", "A fruit")
cache.Set(ctx, "application", "A software program")
value, matchedKey, score, found := cache.GetSimilar(ctx, "app")
if found {
fmt.Printf("Found: %s (matched: %s, similarity: %.2f)\n", value, matchedKey, score)
}cache := synapse.New[string, string]()
ctx1 := synapse.WithNamespace(context.Background(), "tenant1")
ctx2 := synapse.WithNamespace(context.Background(), "tenant2")
cache.Set(ctx1, "config", "tenant1's config")
cache.Set(ctx2, "config", "tenant2's config")
if value, found := cache.Get(ctx1, "config"); found {
fmt.Println(value) // Output: tenant1's config
}cache := synapse.New[string, string](
synapse.WithTTL(5 * time.Minute),
)
cache.Set(ctx, "temp-key", "temporary value")
// Entry expires after 5 minutesimport "github.com/kolosys/synapse/eviction"
lru := eviction.NewLRU(1000)
cache := synapse.New[string, string](
synapse.WithMaxSize(1000),
synapse.WithEviction(lru),
)Synapse uses sharding to distribute keys across multiple partitions, reducing lock contention and improving concurrent performance. Each shard operates independently with its own:
sync.RWMutexfor thread-safe access- Hash map for O(1) exact lookups
- Key slice for similarity searches
- Eviction policy tracker
Exact lookups (Get) route to a single shard using FNV-1a hashing. Similarity searches (GetSimilar) search across all shards sequentially, respecting context cancellation.
- Exact lookups: O(1) average case per shard
- Similarity search: O(n) per shard where n is the number of keys
- Sharding: More shards improve concurrency but increase overhead
- Recommendation: Start with 16 shards, adjust based on workload
Each cache entry stores the key, value, timestamps, access count, expiration time, metadata map, and namespace string. Plan capacity based on your key/value sizes.
MIT License - see LICENSE file for details.