Ajala is a location-based social app that helps travellers and locals find meetups, events, plans and groups. It launches in March 2027, and I’m the lead engineer, mainly on the backend.
The target was clear from the start: support 100,000 concurrent users at launch. Planning for that doesn’t mean buying the biggest of everything. It means working out which numbers change the design, and which parts of the system are allowed to be slightly wrong.
Start with the numbers that change the design
Before choosing how anything would work, I estimated the load. If each active user makes a request every 10 seconds, 100,000 users means 10,000 requests a second, so I planned for 20,000 at peak. Drag the slider to see how the plan responds.
Assumes a request every 10 seconds per user, doubled for peak, and 90% of reads served from cache.
Each figure led to a decision:
- Reads far outnumber writes. With 90% of hot reads served from cache, the database sees about 2,000 queries a second at peak. One writer and two readers can handle that.
- Every user holds a socket open. At 15,000 to 20,000 connections per node, 100,000 sockets means six realtime nodes plus headroom.
- A city’s feed is small. At no more than about 500 upcoming posts per city, one cached candidate set per city is enough, and each person’s ranking can happen in memory.
Decide where being slightly wrong is acceptable
Not everything needs the same guarantee. If the feed is a few seconds out of date, nobody is harmed, so it serves slightly stale data rather than an error.
RSVP capacity, coupon usage and subscription entitlement are different. Being wrong there means two people holding one spot, or a coupon used past its limit. Those stay strongly consistent, which is also why there’s a single write region rather than several.
The last spot
Taking a spot is one atomic update on the post. The count only goes up if it’s still below capacity, so the check and the change can’t be separated by another request.
When a crowd arrives at one post, the first requests take turns for one short transaction each. Once the post is full the update matches nothing, takes no lock, and everyone else gets an answer straight away.
Giving a spot back promotes the oldest person on the waitlist in the same transaction, so there’s no timer or background job that can fall behind.
Decide what not to build yet
Thinking at scale also means knowing what can wait. Launch is one region, with a clear plan for read replicas in other regions when they’re needed. There’s no Redis counter or waiting room in front of a crowded post, because the design only calls for one above a few hundred joins a second on a single post.
Keeping costs down was a requirement too, so the system starts on the smallest managed footprint with autoscaling. Cloud-specific code stays in one layer, so the hosting can move without rewriting the application.
Where it stands
These are design targets, not production measurements, since the app hasn’t launched. The assumptions are deliberately conservative, and they’ll be checked against real analytics once people are using it.