Go + pgx pool + Postgres,默认 Read Committed,每次取款一个事务。余额 1000,daily_limit 50,20 个 goroutine 各取 10。我预期 5 次成功,结果是 7、8 或 9 次。今天的 sum(amount) 最后是 70 到 90。MaxConns=1 时正好是 5。
余额的保护看起来没问题。余额 15 的时候,一个成功,其余的 UPDATE 只返回零行,不会阻塞也不会报错。
INSERT INTO withdrawals (account_id, amount)
SELECT $1, $2
WHERE (SELECT coalesce(sum(amount), 0)
FROM withdrawals
WHERE account_id = $1 AND day = current_date) + $2
<= (SELECT daily_limit FROM accounts WHERE id = $1)
UPDATE accounts
SET balance = balance - $2
WHERE id = $1 AND balance >= $2
Postgres 对 INSERT ... SELECT 的 WHERE 会像对 UPDATE 那样重新检查吗?还是说这是两回事?日志里没看到异常,-race 也是干净的。
我在看的隔离文档在这里 https://www.postgresql.org/docs/current/transaction-iso.html
从英语翻译·查看原文Go + pgx pool + Postgres, default Read Committed, one transaction per withdrawal. Balance 1000, daily_limit 50, 20 goroutines each withdrawing 10. I expect 5 successes and get 7, 8 or 9. sum(amount) for today ends up 70 to 90. With MaxConns=1 it's exactly 5.
The balance guard looks fine. With balance 15, one wins and the rest just get zero rows from the UPDATE. They don't block or error.
INSERT INTO withdrawals (account_id, amount)
SELECT $1, $2
WHERE (SELECT coalesce(sum(amount), 0)
FROM withdrawals
WHERE account_id = $1 AND day = current_date) + $2
<= (SELECT daily_limit FROM accounts WHERE id = $1)
UPDATE accounts
SET balance = balance - $2
WHERE id = $1 AND balance >= $2
Does Postgres recheck the WHERE for the INSERT ... SELECT the way it does for the UPDATE? Or are these two separate things? Logging shows nothing odd, and -race is clean.
Isolation docs I'm reading: https://www.postgresql.org/docs/current/transaction-iso.html