Why I Used Pessimistic Locking to Stop Double-Bookings in AgriLease
A look at the concurrency bug that nearly broke AgriLease's booking engine during peak season, and why optimistic locking wasn't enough to fix it.
AgriLease is a sharing-economy platform for agricultural machinery in Sri Lanka — think of it as a booking system where dozens of farmers can be racing to reserve the same tractor for the same narrow planting window. That's a concurrency problem hiding inside what looks like a simple booking form.
Early on, two requests could both read 'this tractor is free on the 14th', both pass validation, and both write a booking row — a classic race condition. Optimistic locking (checking a version number before committing) reduced the failures but didn't eliminate them under real load, because the retry logic on the client just tried the same doomed booking again.
The fix was pessimistic locking at the database level: `SELECT ... FOR UPDATE` on the relevant availability row for the duration of the booking transaction, so a second request is forced to wait rather than race. It costs a little throughput under contention, but for a resource with hard physical availability — a single tractor can only be in one field at a time — correctness matters far more than raw concurrency.
The bigger lesson: not every problem needs the trendier solution. Optimistic locking is usually the right default for low-contention writes, but the moment a resource is physically singular and demand spikes at the same time (peak planting season, everyone booking the same three tractors), pessimistic locking is the boring, correct answer.