When Redis Stops Being Enough
I built StrikeIt with Upstash Redis as the data store. The choice made sense at the time - free tier, zero setup, HTTP-based so I could hit it from anywhere. I stored everything under one key: a JSON blob with all the lists and tasks serialized inside. It worked fine for a week.
Then it didn't.
The first sign was bandwidth. Every API call - even one that only needed a single task - loaded the entire blob. The more data accumulated, the worse it got. Writes were replace-the-whole-blob operations. Race conditions were one concurrent request away.
I stored data like this:
SET user:123:data '{"lists": [{"id": "abc", "tasks": [...hundreds of tasks...]}]}'
That's it. One key. Every request deserializes the whole thing, every write re-serializes it.
The actual problem: I was treating Redis like a database when it's a cache. It has no real query primitives, no indexes on arbitrary fields, no joins. It's fast at what it does - key lookups, expiring tokens, atomic increments. For relational data with query patterns, it's the wrong tool.
Switching to Supabase was straightforward. Per-row storage meant I could query exactly what I needed. The schema ended up being:
create table items (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users,
content text not null,
done boolean default false,
created_at timestamptz default now()
);
Supabase realtime came for free. Instead of polling every 2 seconds, the client subscribes to row changes. One tab marks something done, every other tab sees it immediately. That replaced about 30 lines of polling code.
The honest tradeoffs: Supabase on the free tier has cold starts. The first request after inactivity is noticeably slow - Redis doesn't have that. And Redis is genuinely good for rate limiting; I still use it for that in other projects. The issue was using it as the source of truth for user data.
What I'd do differently: ask earlier what shape the data is. If the answer is "rows with foreign keys," use a database. If the answer is "ephemeral counters," Redis is the right call. I just didn't ask the question.