Persona: You are a Go engineer who treats caching as a system design decision. You choose eviction algorithms based on measured access patterns, size caches from working-set data, and always plan for expiration, loader failures, and monitoring.
Using samber/hot for In-Memory Caching in Go
Generic, type-safe in-memory caching library for Go 1.22+ with 9 eviction algorithms, TTL, loader chains with singleflight deduplication, sharding, stale-while-revalidate, and Prometheus metrics.
Official Resources:
This skill is not exhaustive — refer to library documentation and code examples for more information:
- For Go package docs, symbols, versions, importers, and known vulnerabilities, → See
samber/cc-skills-golang@golang-pkg-go-dev skill (godig), preferred over Context7 for Go package facts.
- To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See
samber/cc-skills-golang@golang-gopls skill (gopls).
- Context7 remains a fallback for docs not indexed on pkg.go.dev.
go get -u github.com/samber/hot
Algorithm Selection
Pick based on your access pattern — the wrong algorithm wastes memory or tanks hit rate.
| Algorithm |
Constant |
Best for |
Avoid when |
| W-TinyLFU |
hot.WTinyLFU |
General-purpose, mixed workloads (default) |
You need simplicity for debugging |
| LRU |
hot.LRU |
Recency-dominated (sessions, recent queries) |
Frequency matters (scan pollution evicts hot items) |
| LFU |
hot.LFU |
Frequency-dominated (popular products, DNS) |
Access patterns shift (stale popular items never evict) |
| TinyLFU |
hot.TinyLFU |
Read-heavy with frequency bias |
Write-heavy (admission filter overhead) |
| S3FIFO |
hot.S3FIFO |
High throughput, scan-resistant |
Small caches (<1000 items) |
| ARC |
hot.ARC |
Self-tuning, unknown patterns |
Memory-constrained (2x tracking overhead) |
| TwoQueue |
hot.TwoQueue |
Mixed with hot/cold split |
Tuning complexity is unacceptable |
| SIEVE |
hot.SIEVE |
Simple scan-resistant LRU alternative |
Highly skewed access patterns |
| FIFO |
hot.FIFO |
Simple, predictable eviction order |
Hit rate matters (no frequency/recency awareness) |
Decision shortcut: Start with hot.WTinyLFU. Switch only when profiling shows the miss rate is too high for your SLO.
For detailed algorithm comparison, benchmarks, and a decision tree, see [Algorithm Guide](./references/algorithm-guide.md).
Core Usage
Basic Cache with TTL
import "github.com/samber/hot"
cache := hot.NewHotCache[string, *User](hot.WTinyLFU, 10_000).
WithTTL(5 * time.Minute).
WithJanitor().
Build()
defer cache.StopJanitor()
cache.Set("user:123", user)
cache.SetWithTTL("session:abc", session, 30*time.Minute)
value, found, err := cache.Get("user:123")
Loader Pattern (Read-Through)
Loaders fetch missing keys automatically with singleflight deduplication — concurrent Get() calls for the same missing key share one loader invocation:
cache := hot.NewHotCache[int, *User](hot.WTinyLFU, 10_000).
WithTTL(5 * time.Minute).
WithLoaders(func(ids []int) (map[int]*User, error) {
return db.GetUsersByIDs(ctx, ids) // batch query
}).
WithJanitor().
Build()
defer cache.StopJanitor()
user, found, err := cache.Get(123) // triggers loader on miss
Capacity Sizing
Before setting the cache capacity, estimate how many items fit in the memory budget:
- Estimate single-item size — estimate size of the struct, add the size of heap-allocated fields (slices, maps, strings). Include the key size. A rough per-entry overhead of ~100 bytes covers internal bookkeeping (pointers, expiry timestamps, algorithm metadata).
- Ask the developer how much memory is dedicated to this cache in production (e.g., 256 MB, 1 GB). This depends on the service's total memory and what else shares the process.
- Compute capacity —
capacity = memoryBudget / estimatedItemSize. Round down to leave headroom.
Example: *User struct ~500 bytes + string key ~50 bytes + overhead ~100 bytes = ~650 bytes/entry
256 MB budget → 256_000_000 / 650 ≈ 393,000 items
If the item size is unknown, ask the developer to measure it with a unit test that allocates N items and checks runtime.ReadMemStats. Guessing capacity without measuring leads to OOM or wasted memory.
Common Mistakes
- Forgetting
WithJanitor() — without it, expired entries stay in memory until the algorithm evicts them. Always chain .WithJanitor() in the builder and defer cache.StopJanitor().
- Calling
SetMissing() without missing cache config — panics at runtime. Enable WithMissingCache(algorithm, capacity) or WithMissingSharedCache() in the builder first.
WithoutLocking() + WithJanitor() — mutually exclusive, panics. WithoutLocking() is only safe for single-goroutine access without background cleanup.
- Oversized cache — a cache holding everything is a map with overhead. Size to your working set (typically 10-20% of total data). Monitor hit rate to validate.
- Ignoring loader errors —
Get() returns (zero, false, err) on loader failure. Always check err, not just found.
Best Practices
- Always set TTL — unbounded caches serve stale data indefinitely because there is no signal to refresh
- Use
WithJitter(lambda, upperBound) to spread expirations — without jitter, items created together expire together, causing thundering herd on the loader
- Monitor with
WithPrometheusMetrics(cacheName) — hit rate below 80% usually means the cache is undersized or the algorithm is wrong for the workload
- Use
WithCopyOnRead(fn) / WithCopyOnWrite(fn) for mutable values — without copies, callers mutate cached objects and corrupt shared state
For advanced patterns (revalidation, sharding, missing cache, monitoring setup), see [Production Patterns](./references/production-patterns.md).
For the complete API surface, see [API Reference](./references/api-reference.md).
If you encounter a bug or unexpected behavior in samber/hot, open an issue at <https://github.com/samber/hot/issues>.
Cross-References
- → See
samber/cc-skills-golang@golang-performance skill for general caching strategy and when to use in-memory cache vs Redis vs CDN
- → See
samber/cc-skills-golang@golang-observability skill for Prometheus metrics integration and monitoring
- → See
samber/cc-skills-golang@golang-database skill for database query patterns that pair with cache loaders
- → See
samber/cc-skills@promql-cli skill for querying Prometheus cache metrics via CLI