![]() | |
![]() |
| | Thread Tools | Display Modes |
#1
| |||
| |||
|
#2
| |||
| |||
|
|
Hi All, begin; update pool set token = 'a' where id = (select id from pool where token = null limit 1); commit; ...and rest assured that the subquery would lock the row, and not allow any other transaction to alter the row until done. Appears I'm not too clever after all. Postgresql does exactly what the documentation states: When I execute 2 concurrent transactions, the second one retrieves the same column that the first transaction already got, but waits for the first one to commit (row lock). Now since the second one executed the subquery while the first one is still not commited, they will both get the same id, and once the first transaction is commited, the second one will overwrite the newly updated value a second time. Result, both transactions believes they've succecfully occupied a unique token, but the database only shows a single token being occupied. The basic problem here must have been solved a hundred times over, so I'm hopefull that someone in this group can show me the error of my path, and perhaps a better solution. |
#3
| |||
| |||
|
|
Fredrik wrote: Result, both transactions believes they've succecfully occupied a unique token, but the database only shows a single token being occupied. [That's] Easy [to avoid]: check for null again. update pool set token = 'a' where id = (select id from pool where token is null limit 1) and token is null; |
|
Better: select for update. update pool set token = 'a' where id = (select id from pool where token = null limit 1 for update) |
#4
| |||
| |||
|
|
update pool set token = 'a' where id = (select id from pool where token is null limit 1) and token is null; If zero rows were updated then try again. You won't be able to tell the instance when there are no free tokens from when there is a clash. |
#5
| |||
| |||
|
|
Jasen Betts wrote: update pool set token = 'a' where id = (select id from pool where token is null limit 1) and token is null; If zero rows were updated then try again. You won't be able to tell the instance when there are no free tokens from when there is a clash. Obviously in real life it turns out that this solution is trivial to DOS. Any time you have more than one concurrent transaction the last transaction in is guaranteed to get nothing. The subquery select will return the same results to both transactions until the first transaction commits. Retrying again on the application level only increases the risk of contention. As a temporary workaround I've added a "LOCK TABLE pool" before the query. It works, but I'm not too happy about it. If anyone got another less drastic alternative, I'm very interested. |
![]() |
| Thread Tools | |
| Display Modes | |
| |