My solution for handling race conditions by using Laravel Redis Lock & InnoDB Record Lock. This project includes stress tests to validate the effectiveness of the implemented solution.
flowchart TB
Client --> |Submit signup data| Server
Server --> DB[(Database)]
DB --> |Register a member| memberTable[/Member/]
memberTable --> |Get available coupon and mark as used| couponTable[/Coupon/]
public sendCoupon(Member $member) {
$coupon = Coupon::where('member_id', null)->get()->first();
if (is_null($coupon)) {
throw new Exception('no issuable coupon', 1);
}
try {
DB::beginTransaction();
$coupon->member_id = $member->id;
$coupon->redeemed_at = date('Y-m-d H:i:s');
$coupon->save();
DB::commit();
} catch (Throwable $th) {
DB::rollback();
throw new Exception('send coupon failed', 1);
}
}
- Docker
-
By running commands below, docker will build 4 containers: app (including PHP 8.1, Golang 1.18, k6, xk6), nginx, mysql, redis
git clone https://github.com/uhcakip/coupon-race-condition.git cd path/to/project make start
-
Access Docker container and build k6 with extension
make exec-app cd k6 xk6 build --with github.com/szkiba/xk6-faker
-
Running API stress test
./k6 run scripts/sign-up.js