As ShopEase moved toward a more production-oriented backend, one important problem needed to be solved:
What happens when multiple users try to purchase the same product at exactly the same time?
In a normal single-user test, stock deduction looks simple. But in a real e-commerce system, multiple requests can reach the backend simultaneously and try to update the same inventory.
That creates a serious problem: overselling.
For Module 32, I focused on multithreading, concurrent stock updates, optimistic locking, pessimistic locking, and atomic stock deduction.
⚡ The Problem: Concurrent Stock Updates
Suppose a product has:
Stock = 10
Now imagine 50 users trying to purchase that product simultaneously.
Without proper concurrency control, multiple threads could read the same stock value before any of them updates it.
For example:
Thread 1 → reads stock = 1
Thread 2 → reads stock = 1
Thread 1 → deducts stock
Thread 2 → deducts stock
Both threads may believe that the product is available.
This is a classic race condition, and in an e-commerce system it can result in selling more units than actually exist.
🧵 Multithreading & Stress Testing
To reproduce this situation instead of relying on normal sequential testing, I introduced concurrent stress testing.
I used 50 concurrent threads attempting to perform stock-related operations.
The purpose wasn't simply to make multiple threads run at once.
The real objective was to verify whether the database and application could correctly handle simultaneous requests accessing the same inventory.
This made the concurrency problem much easier to observe and debug.
🔒 Pessimistic Locking
For pessimistic locking, I implemented:
LockModeType.PESSIMISTIC_WRITE
in the ProductRepository.
The idea is straightforward:
When one transaction is modifying a product row, other transactions shouldn't be allowed to simultaneously modify that same row.
I also configured the MariaDB dialect so Hibernate could generate the appropriate database-level row locking behavior using FOR UPDATE.
Conceptually, the flow becomes:
Transaction 1
↓
Lock Product Row
↓
Check Stock
↓
Deduct Stock
↓
Commit
↓
Release Lock
Another transaction attempting to modify the same row has to wait until the existing lock is released.
This provides strong protection for critical inventory updates.
🧠 Optimistic Locking
I also tested optimistic locking using JPA's:
Instead of immediately locking the database row, optimistic locking assumes that conflicts are relatively uncommon.
Each update checks whether the entity version is still the expected version.
If another transaction has already modified the row, the version changes and the conflicting update can be detected.
Conceptually:
Thread 1 → Version 5 → Update → Version 6 ✅
Thread 2 → Version 5 → Update
↓
Version mismatch ❌
This approach is useful when you want to detect concurrent modifications without holding database locks throughout the transaction.
🛡️ Atomic Stock Deduction
Locking alone isn't the complete solution.
The actual stock operation also needs to be handled carefully so that the availability check and deduction happen safely within the transaction.
The goal was:
Check stock
↓
Stock available?
↓
Atomic deduction
↓
Commit
If sufficient stock isn't available, the purchase must fail rather than allowing the stock value to become invalid.
🧪 50-Thread Concurrent Testing
After implementing both approaches, I performed concurrent testing with 50 threads.
The tests were used to verify:
Concurrent access to the same product
Optimistic locking conflicts
Pessimistic row locking
Atomic stock deduction
Collision handling
Prevention of negative stock
Prevention of overselling
The final result was zero overselling during the concurrent stress tests.
🔍 What I Learned
This module changed the way I think about backend development.
A piece of code can work perfectly when one request executes at a time and still fail when multiple requests execute simultaneously.
Concurrency introduces problems such as:
Race conditions
Lost updates
Concurrent modification conflicts
Database locking
Transaction boundaries
Thread synchronization
The important lesson was:
Correctness in a backend isn't only about what happens during one request. It's also about what happens when many requests happen at the same time.
Module 32 gave ShopEase practical protection against one of the most important problems in e-commerce systems: selling inventory that doesn't actually exist.
Tech Stack
Java • Spring Boot • Spring Data JPA • Hibernate • MariaDB • Multithreading • Optimistic Locking • Pessimistic Locking • Transactions
Top comments (0)