zlacker

[return to "Choose Postgres queue technology"]
1. aduffy+82[view] [source] 2023-09-24 20:41:49
>>bo0tzz+(OP)
For several projects I’ve opted for the even dumber approach, that works out of the box with every ORM/Query DSL framework in every language: using a normal table with SELECT FOR UPDATE SKIP LOCKED

https://www.pgcasts.com/episodes/the-skip-locked-feature-in-...

It’s not “web scale” but it easily extends to several thousand background jobs in my experience

◧◩
2. matsem+x2[view] [source] 2023-09-24 20:44:27
>>aduffy+82
I've done even simpler without locks (as no transaction logic), where I select a row, and then try to update a field about it being taken. If 1 row is affected, it's mine. If 0, someone else did it before me and I select a new row.

I've used this for tasks at big organizations without issue. No need for any special deployments or new infra. Just spin up a few worker threads in your app. Perhaps a thread to reset abandoned tasks. But in three years this never actually happened, as everything was contained in try/catch that would add it back to the queue, and our java app was damn stable.

◧◩◪
3. refibr+Lq[view] [source] 2023-09-25 00:39:07
>>matsem+x2
PSA: This is a read-modify-write pattern, thus it is not safe under concurrency unless a transaction isolation level of SERIALIZABLE is specified, or some locking mechanism is used (select for update etc).
◧◩◪◨
4. derede+RS[view] [source] 2023-09-25 06:27:21
>>refibr+Lq
The part about checking the number of affected rows hints at using `UPDATE ... WHERE ...` which should act as an atomic CAS regardless of isolation level.

Edit: To clarify, I mean `SELECT id WHERE used = 0` followed by `UPDATE ... SET used = 1 WHERE id = ... AND used = 0`

◧◩◪◨⬒
5. Richie+PV[view] [source] 2023-09-25 06:59:19
>>derede+RS
This works fine as long as you’re happy to do the same task multiple times. I.e. the task is idempotent and cheap.
◧◩◪◨⬒⬓
6. kdps+A21[view] [source] 2023-09-25 08:15:40
>>Richie+PV
I don't get it :(. Why could the same task be executed more than once? From my understanding, if the UPDATE is atomic, only one worker will be able to set `used = 1`. If the update statement is not successful (affected != 1), then the worker should drop the task and do another select.
◧◩◪◨⬒⬓⬔
7. klause+L61[view] [source] 2023-09-25 08:59:54
>>kdps+A21
With a transaction isolation level below SERIALIZABLE you can have two transactions that both read the old row (with `used = 0`) at the time they perform the update (but before they commit the transaction). In that case, both transactions will have performed an update (rows affected = 1).

Why would both transactions see `used = 0`? The DB server tries to isolate transactions and actively hides effects of other transactions that have not committed yet.

◧◩◪◨⬒⬓⬔⧯
8. singro+zd1[view] [source] 2023-09-25 10:11:33
>>klause+L61
This is not true in postgres. When the second transaction tries to update the row, it will wait for the first transaction to commit first and then recheck the WHERE.

https://www.postgresql.org/docs/current/transaction-iso.html...

[go to top]