Had yet another conversation yesterday with a team burning themselves on a Redis cache misconfiguration. Hot keys, thundering herd, memory policy oversights — the usual scars. One of the replies on the HN thread mentioned something that keeps showing up in these discussions: they just use a database table + filesystem, no Redis, no Memcached.
Sounded like a lazy joke. It isn't.
The setup
You already have a database. You already have a filesystem. You can build a perfectly functional caching layer with both.
The database table looks something like:
CREATE TABLE cache_entries (
cache_key TEXT PRIMARY KEY,
expires_at TIMESTAMPTZ,
computing BOOLEAN DEFAULT FALSE
);
The filesystem holds the actual cached values — call them /var/cache/myapp/<key>. Reads check the filesystem first; if the file is stale or missing, the DB is consulted.
What you actually get
Thundering herd protection. This is the part teams reach for Redis for, and it's the easiest win. Use a database row lock — SELECT FOR UPDATE — so that when a cache miss hits, only one process computes the result while others wait. Postgres handles this fine. The filesystem doesn't need to know about it.
Coordinated expiration. The DB holds the TTL. Workers can query expires_at before even touching the filesystem, so you don't serve stale content.
No extra daemon. No Redis process to babysit, no persistence configuration to get wrong, no memory limit to tune.
Persistence you actually control. The filesystem is on disk. If you want to survive restarts, you already have that. You don't have to configure appendfsync always and pray.
The part that bites
This is not Redis. If your cache layer needs to be shared across many application servers, you're back to a central store — the DB becomes that bottleneck and you haven't actually solved anything. This pattern works best on a single server or when the DB is already the shared coordinator anyway.
For high-TPS workloads (thousands of cache reads per second from many nodes) you'll still want something designed for the job. But most internal tooling, sidekiq-style job caching, and slow-query caching isn't that workload.
When it's worth considering
- You're already running a database
- Your cache is per-server or your DB is already shared
- You want to reduce operational dependencies
- You're building something that doesn't need Redis' data structures
The comment that stuck with me from the thread: "Using a db table as a k/v store + the FS can do so much before even considering paying the price of setting up a dedicated caching store."
He's right. The boring choice is boring for a reason.
Top comments (0)