ttlcache
A generic, thread-safe in-memory cache for Go with per-item TTL, automatic expiration, and loader-based cache-miss handling.
Repository Health
Technical Analysis
ttlcache is a lightweight, generic in-memory cache for Go that automatically expires items based on a configurable time-to-live. Built on Go’s type parameters, it stores any comparable key alongside any value type without reflection or interface{} boxing, and every operation is safe for concurrent use across goroutines.
Beyond basic get/set/delete semantics, ttlcache adds the pieces most hand-rolled caches skip: a background cleanup goroutine that proactively evicts expired entries, insertion/update/eviction event hooks for reacting to cache changes, a pluggable Loader interface (with duplicate-call suppression via singleflight) for lazily populating cache misses, and capacity limits based on either item count or a custom-calculated cost function. It’s used inside production systems like TiDB, HashiCorp Vault, Tailscale, and the OpenTelemetry Collector as their in-process caching layer.
What You Get
- Generic Cache[K,V] type - type-safe storage for any comparable key and any value, no interface{} casts.
- Per-item and cache-wide TTL - set a default TTL at construction, override it per Set() call, or opt out entirely with NoTTL.
- Automatic or manual expiration - start a background cleanup goroutine with Start(), or call DeleteExpired() on your own schedule.
- Capacity and cost-based eviction - cap by item count with WithCapacity or by a custom memory/cost function with WithMaxCost.
- Loader interface with call suppression - lazily populate misses via a Loader, optionally wrapped in NewSuppressedLoader to collapse concurrent loads for the same key into one.
Common Use Cases
- Caching database query results in a web service to cut round-trips to Postgres/MySQL under read-heavy load.
- Rate-limiter or session-token stores that need automatic expiry without standing up a separate Redis instance.
- In-memory DNS/HTTP response caching for proxies and API gateways.
- Deduplicating expensive external API calls with the suppressed loader so only one goroutine fetches per key.
- Memoizing computed values inside CLI tools or batch jobs where result reuse matters but persistence doesn’t.
Under The Hood
Architecture The cache is a single-package design (package ttlcache) built around a synchronized map (items.values) backed by a container/list doubly-linked list for LRU ordering plus a heap-based expirationQueue for O(log n) access to the next-expiring item. The Cache[K,V] struct centralizes all state — item storage, cost tracking, and event subscriber maps (insertion/update/eviction, each independently mutex-guarded) — alongside a stop channel/goroutine (Start/Stop) driven by a single timer fed via a buffered timer channel. Public methods wrap private lowercase helpers that assume the caller already holds the items mutex, keeping locking discipline centralized rather than scattered across the API. Options use the functional-options pattern via an Option[K,V] interface wrapping closures, decoupling construction-time configuration from the Cache struct itself, while each Item keeps its own mutex for safe external reads from event callbacks. It reads as a well-organized monolith rather than a layered system: LRU ordering, expiration ordering, and cost tracking are all threaded through nearly every operation on the core struct.
Tech Stack Standard-library-first: go.mod declares only three direct dependencies — golang.org/x/sync (singleflight, for the suppressed loader), stretchr/testify (test assertions), and go.uber.org/goleak (goroutine-leak detection in tests) — plus one indirect YAML dependency. There is no web framework, ORM, or database; container/list and container/heap from the standard library back the LRU list and expiration priority queue respectively. CI (GitHub Actions) runs tests with the race detector and randomized ordering, reporting coverage to Coveralls, and Dependabot watches both Go modules and Actions dependencies daily. The library ships purely as a Go module with no build tooling of its own; two in-repo example applications demonstrate embedding it behind a database layer and an HTTP proxy.
Code Quality Testing is extensive and taken seriously: the core test file alone is substantial, with dedicated test files for the expiration queue, items, and options, plus a benchmark suite for throughput. CI runs with the race detector, shuffled test ordering, and goleak-based goroutine-leak assertions — a strong practical safeguard for a concurrency-heavy library. Error handling favors idiomatic Go patterns (nil/zero-value plus ok booleans) since there’s little external I/O to fail; the one place an error could otherwise surface is explicitly and deliberately discarded, with an inline comment explaining why. Naming is consistent and idiomatic throughout, with clearly marked unsafe-suffixed helpers documenting which methods assume a lock is already held, and generics eliminate interface{} from every public signature. No dedicated linter configuration was found, though compilation checks and race-enabled CI cover a meaningful part of that gap.
What Makes It Unique Most small Go TTL caches offer basic get/set/expire semantics. This library’s differentiators are the combination of a heap-based expiration queue for efficient next-to-expire lookups rather than full scans or per-key timers, optional cost-based eviction that lets callers bound cache size by estimated memory rather than raw item count, and a suppressed-loader wrapper that collapses duplicate concurrent cache-miss loads into a single execution — a pattern usually left for callers to build themselves. It doesn’t invent a new eviction algorithm, and each individual idea exists elsewhere, but combining them behind one clean generic API is a genuine ergonomic step up from more minimal alternatives, reflected in its adoption inside large production systems.
Used by 5 apps in this directory
authentik
Authentication · Security
The self-hosted Identity Provider that replaces Okta, Auth0, and Entra ID with a unified SSO platform supporting SAML, OAuth2/OIDC, LDAP, RADIUS, and WebAuthn.
Navidrome
File Storage
Run your own personal Spotify — stream your entire music collection from any device, anywhere, forever.
opencloud
File Storage
Open source file management and collaboration platform that keeps your data under your control, no database required.
tau
Devops
Open-source, Git-native platform-as-a-service for building, deploying, and scaling fullstack apps on your own infrastructure with no DevOps required.
TiDB
Databases · AI Development
AI-Native Distributed SQL Database for Agentic Workloads