prerequisites:
- You should install Docker in your machine
Running the App (Using Docker)
-
Clone this repository:
git clone https://github.com/nittsuj/order-system.git cd order-system -
Build and run the
container:docker compose up --build
-
Access the application
- The API is available at:
http://localhost:8000
- The API is available at:
Once the container is running, you can access the service at:
- Web/API:
http://localhost:8000 - API Documentation:
http://localhost:8000/docsPowered by FastAPI (Swagger UI)
Let's define what is a race condition first
Race condition is a condition where 2 or more threads and/or process
can access shared data and try to change the data at the same time,
resulting in unforeseen and unintentional outcomes.
My Solution
I used Pessimistic Concurrency Control with SQLAlchemy's with_for_update(), which acts on belief that conflict between transacitons is probable, and thus it uses locks to prevent record and conflict in the database.
How it works
- When a User attempts to purchase a product, the
with_for_update()applies a row-level lock. - This ensures that, if multiple users try to buy the same product at the same time
- Only one can proceed at the transaction at a tiem, while others wait for the lock to be released
- This prevents overselling and maintains data consistency
Trade Offs
There are conflicts that can arose with using Pessimistic Control, such as Deadlock (state of 2 or more process are stuck forever because each is waiting for a resource held by another).
- Create Product
curl -X POST "http://localhost:8000/products/" \
-H "Content-Type: application/json" \
-d '{"name": "MacBook Pro", "price": 25000000, "stock": 5}'- Create User
curl -X POST "http://localhost:8000/users/" \
-H "Content-Type: application/json" \
-d '{"username": "john_doe", "email": "john@example.com"}'- Buy Product
curl -X POST "http://localhost:8000/products/buy/1?user_id=1&quantity=1"{"status":"success","order_id":4,"message":"Order placed! Processing in background.","remaining_stock":3}
This is just a local result with my previous test
- View Logs
docker logs -f ecommerce_worker[2026-01-19 09:08:55,036: WARNING/ForkPoolWorker-8] Order #4 Processed.
[2026-01-19 09:08:55,048: INFO/ForkPoolWorker-8] Task worker.processing_order[48765cde-a561-4783-a33c-c28239c13cd6] succeeded in 5.0179524019986275s: 'Order #4 Processed.'
This is just a local result with my previous test
Tested using Apache Bench (ab) with 1000 requests and 200 concurrencies against 1 unit remaining from stock
I use Arch Linux as the Linux distro, so my command to download is the following:
The command could be used for Arch/Manjaro
sudo pacman -Syu
sudo pacman -S apacheIf you are using Ubuntu/Debian:
sudo apt update
sudo apt install apache2-utilsit is preinstalled
There is no native installer for Windows.
Please download the binary from Apache Lounge. Extract the ZIP file, navigate to the Apache24/bin folder, and run ab.exe via Command Prompt or PowerShell.
- The following command line will reset a product_id (product)'s stock to 1
docker compose exec db mysql -u root -p ecommerce_db -e "UPDATE products SET stock=1 WHERE id=1;"- The following command executes high-concurrency test using Apache Bench (ab)
Sends 1000 POST request with simultaneous 200 users buying same item at the same time
ab -n 1000 -c 200 -m POST "http://localhost:8000/products/buy/1?user_id=1&quantity=1"resulting in a 1 successful purchase 999 rejected
Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: uvicorn
Server Hostname: localhost
Server Port: 8000
Document Path: /products/buy/1?user_id=1&quantity=1
Document Length: 106 bytes
Concurrency Level: 200
Time taken for tests: 3.319 seconds
Complete requests: 1000
Failed requests: 999
(Connect: 0, Receive: 0, Length: 999, Exceptions: 0)
Non-2xx responses: 999
Total transferred: 179072 bytes
HTML transferred: 26080 bytes
Requests per second: 301.31 [#/sec] (mean)
Time per request: 663.770 [ms] (mean)
Time per request: 3.319 [ms] (mean, across all concurrent requests)
Transfer rate: 52.69 [Kbytes/sec] received

