mirror of
https://github.com/System-End/scraps.git
synced 2026-04-19 22:05:09 +00:00
fix: move refinery stock check inside transaction with row lock
The out-of-stock check was before the transaction, so concurrent requests could race past it. Now locks the shop_items row with FOR UPDATE inside the transaction before checking count.
This commit is contained in:
parent
2fda114b3d
commit
5f92ea1616
1 changed files with 9 additions and 4 deletions
|
|
@ -612,15 +612,17 @@ shop.post('/items/:id/upgrade-probability', async ({ params, headers }) => {
|
|||
|
||||
const item = items[0]
|
||||
|
||||
if (item.count <= 0) {
|
||||
return { error: 'Item is out of stock' }
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await db.transaction(async (tx) => {
|
||||
// Lock the user row to serialize spend operations and prevent race conditions
|
||||
await tx.execute(sql`SELECT 1 FROM users WHERE id = ${user.id} FOR UPDATE`)
|
||||
|
||||
// Lock the item row and check stock atomically
|
||||
const stockCheck = await tx.execute(sql`SELECT count FROM shop_items WHERE id = ${itemId} FOR UPDATE`)
|
||||
if (!stockCheck.rows[0] || (stockCheck.rows[0] as { count: number }).count <= 0) {
|
||||
throw { type: 'out_of_stock' }
|
||||
}
|
||||
|
||||
const boostResult = await tx
|
||||
.select({
|
||||
boostPercent: sql<number>`COALESCE(SUM(${refineryOrdersTable.boostAmount}), 0)`
|
||||
|
|
@ -697,6 +699,9 @@ shop.post('/items/:id/upgrade-probability', async ({ params, headers }) => {
|
|||
return result
|
||||
} catch (e) {
|
||||
const err = e as { type?: string; balance?: number; cost?: number }
|
||||
if (err.type === 'out_of_stock') {
|
||||
return { error: 'Item is out of stock' }
|
||||
}
|
||||
if (err.type === 'max_probability') {
|
||||
return { error: 'Already at maximum probability' }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue