Multi-tenant freemium: keeping the free tier off the database
Free users generate most of the reads and almost none of the revenue. Serving those reads from memory keeps free-tier traffic off the shared database that paying tenants depend on.
Every freemium product ends up with the same shape of traffic. A small number of paying tenants generate the writes and the revenue. A much larger number of free users generate the reads: opening the app, loading a profile, scrolling a list, checking whether anything changed. Roughly ninety-five percent of the calls do not need the database at all, and most of those calls come from people who will never pay.
The shared database problem
Most multi-tenant applications (one codebase and one database serving many customer organisations, each called a tenant) keep every tenant in the same tables, with a tenant identifier on each row. It is the sensible design: one set of tables, one deployment, one backup. It also means every tenant shares the same processor, the same memory and the same queue for the disk on the database server.
That is where free users become expensive. A burst of free sign-ups after a marketing push does not stay in its lane. It queues in front of the paying tenants' queries, and a paying customer's page load slows down because someone on the free plan is scrolling. The database cannot tell which read matters, and the customer paying for a service level notices first.
The instinctive fixes are all costly. A bigger database server raises the ceiling and the bill in one step. Separate databases per tier double the operational work and still leave the free tier hitting a database. Capping how often free users can call the service protects paying tenants by making the free product worse.
Serve the free tier from memory
A different approach is to change where reads are answered. Put an API in front of the database, keep a copy of the frequently read data in Redis (a memory store that answers in about a millisecond), and serve reads from there. Writes still go straight to the database, which remains the source of truth. A sync service watches the database for changes and carries every insert, update and delete across to Redis, so the copy is kept in step without the application managing it.
The effect on the free tier is direct. A free user's session is almost entirely reads: their own profile, the content they are browsing, the shared reference data every tenant sees. All of it comes from memory. The database sees that user only when they write something, which for a free user is rare.
The paying tenant's experience changes for the opposite reason. Their reads also come from memory, and the database they share is now doing a fraction of its previous work, most of it writes and the complex queries it is actually good at. Headroom on the same hardware goes up, and the pressure to buy the next server size goes away.
The free tier is a marketing cost you can move from the database bill to the memory bill, where it is far cheaper.
Tenant isolation, honestly
A shared cache raises the same question as a shared database: can one tenant's data leak into another tenant's read? The tenant filter is the boundary. Every read carries the tenant it is for, and the application filters on it through the API's multi-field queries, exactly as its SQL did. Nothing about that boundary moved; only the store answering the query did.
Noisy neighbours are a separate matter. Redis is shared too, so a free tenant hammering reads still consumes memory throughput, but a memory read costs a tiny fraction of a disk read. The burst that once queued in front of paying tenants' database queries is now absorbed by a store built for exactly that.
The limits worth knowing
This pattern does not make writes cheaper. If free users generate heavy writes, such as uploading content or logging every action, the database still carries that load and needs to be sized for it. Memory-fronted reads help most when reads vastly outnumber writes, which describes the free tier of almost every marketplace, social app or travel product.
Freshness is bounded, not instant. On SQL Server the sync reads change tracking on a short interval; on PostgreSQL and MongoDB changes are streamed. A free user may see a value a moment older than the database holds, which is fine for a feed or a profile and not fine for a balance. Route the handful of reads that must be current through the database and leave the rest in memory.
And if Redis is unreachable, reads fall back to the database automatically. The application does not change, and the load goes back to where it was before, which is a state you have already survived.
What changes on the bill and in the codebase
The database line of the cloud bill falls because the database server no longer has to be sized for the free tier's read peaks. That line is usually the biggest one, and this is where a cut of around seventy percent on it comes from, instead of growth with every sign-up wave.
The codebase gets smaller, not larger. There is no cache code, no time-to-live setting and no invalidation logic to write, because the sync keeps Redis correct from the database's own change signals. Engineers call the API for the rows they need, and the tenant filter is the same one they always wrote.
Where redfly fits
redfly is that API and sync service, run against your existing SQL Server database in your own cloud account, with per-tenant read paths served from Redis and no cache code in your codebase. We put it in place with design partners against their existing tables.