Implement FOR UPDATE SKIP LOCKED #6251

Open
opened 2026-01-22 15:29:34 +01:00 by admin · 9 comments
Owner

Originally created by @BenMorel on GitHub (Jun 19, 2019).

Feature Request

Q A
New Feature yes
RFC no
BC Break no

Summary

As far as I can see, there is currently no way to perform a SELECT FOR UPDATE SKIP LOCKED (MySQL, PostgreSQL) with Doctrine.

This is really useful, for example when lauching several concurrent workers picking jobs from a single table, as it effectively prevents two workers from getting the same job, and automatically and immediately makes the job available again in case the transaction is aborted.

Would you be willing to add this feature? This could be implemented this way:

$em->find($id, LockMode::PESSIMISTIC_WRITE | LockMode::SKIP_LOCKED);

I can open a PR if you agree with this.

Originally created by @BenMorel on GitHub (Jun 19, 2019). ### Feature Request <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | New Feature | yes | RFC | no | BC Break | no #### Summary As far as I can see, there is currently no way to perform a `SELECT FOR UPDATE SKIP LOCKED` (MySQL, PostgreSQL) with Doctrine. This is really useful, for example when lauching several concurrent workers picking jobs from a single table, as it effectively prevents two workers from getting the same job, and automatically and immediately makes the job available again in case the transaction is aborted. Would you be willing to add this feature? This could be implemented this way: ```php $em->find($id, LockMode::PESSIMISTIC_WRITE | LockMode::SKIP_LOCKED); ``` I can open a PR if you agree with this.
Author
Owner

@Ocramius commented on GitHub (Jun 22, 2019):

For that specific use-case, I use the RDBMS-specific syntax:

UPDATE
  bernard_messages
SET
  visible = FALSE
WHERE
  id IN (
    SELECT
      id
    FROM
      bernard_messages
    WHERE
      queue = :queue
      AND
      visible = :visible
    ORDER BY
      id ASC
    LIMIT 1
    FOR UPDATE
  )
RETURNING
  id,
  message

Would this be sufficient for your use-case? It seems very much aimed at queue management...

@Ocramius commented on GitHub (Jun 22, 2019): For that specific use-case, I use the RDBMS-specific syntax: ```sql UPDATE bernard_messages SET visible = FALSE WHERE id IN ( SELECT id FROM bernard_messages WHERE queue = :queue AND visible = :visible ORDER BY id ASC LIMIT 1 FOR UPDATE ) RETURNING id, message ``` Would this be sufficient for your use-case? It seems very much aimed at queue management...
Author
Owner

@BenMorel commented on GitHub (Jun 23, 2019):

Hi Marco, I can already do this with native SQL, my request is to be able to perform such a SELECT using the ORM.

Also, your syntax above is Postgres-only (not supported on MySQL), and, at least on MySQL, it kills all concurrency by using FOR UPDATE: a concurrent query would have to wait for this one to release the lock, so it's not a replacement for FOR UPDATE SKIP LOCKED I'm afraid!

@BenMorel commented on GitHub (Jun 23, 2019): Hi Marco, I can already do this with native SQL, my request is **to be able to perform such a SELECT using the ORM**. Also, your syntax above is Postgres-only (not supported on MySQL), and, at least on MySQL, it kills all concurrency by using `FOR UPDATE`: a concurrent query would have to wait for this one to release the lock, so it's not a replacement for `FOR UPDATE SKIP LOCKED` I'm afraid!
Author
Owner

@jusurb commented on GitHub (Nov 22, 2019):

would also love to have this

@jusurb commented on GitHub (Nov 22, 2019): would also love to have this
Author
Owner

@allan-simon commented on GitHub (Dec 17, 2020):

I have a use case which is not queue related , I have a set of payment for which I need to pull the 3rd-party API to know about their last status, and in case of change of status, to notify the end users . I need to rotate over the list of payments , and I don't want to have a "singleton" worker to do this. (much better to have N workers that can scale up and down ) . And for this the FOR UPDATE SKIP LOCKED is very elegant solution to the concurrency problem

@allan-simon commented on GitHub (Dec 17, 2020): I have a use case which is not queue related , I have a set of payment for which I need to pull the 3rd-party API to know about their last status, and in case of change of status, to notify the end users . I need to rotate over the list of payments , and I don't want to have a "singleton" worker to do this. (much better to have N workers that can scale up and down ) . And for this the FOR UPDATE SKIP LOCKED is very elegant solution to the concurrency problem
Author
Owner

@allan-simon commented on GitHub (Dec 17, 2020):

also for reference I've seen this Stackoverflow question https://stackoverflow.com/questions/40586294/doctrine-postgresql-pessimistic-locking-doesnt-throw-pessimisticlockexcepti

@allan-simon commented on GitHub (Dec 17, 2020): also for reference I've seen this Stackoverflow question https://stackoverflow.com/questions/40586294/doctrine-postgresql-pessimistic-locking-doesnt-throw-pessimisticlockexcepti
Author
Owner

@adrianrudnik commented on GitHub (Mar 7, 2021):

Same here, wanted to use it for assigning a ticket to "the next best slot available" while working with pessimistic locks. Both MySQL and PostgreSQL seem to support it, but I can't figuire out how to implement it, even with hints. Is there no node to walk in an SqlWalker/AST to append something at the very end?

@adrianrudnik commented on GitHub (Mar 7, 2021): Same here, wanted to use it for assigning a ticket to "the next best slot available" while working with pessimistic locks. Both MySQL and PostgreSQL seem to support it, but I can't figuire out how to implement it, even with hints. Is there no node to walk in an SqlWalker/AST to append something at the very end?
Author
Owner

@artworx commented on GitHub (Jun 2, 2021):

In my use case, we have multiple short transactions waiting for a big batch. I need to add SKIP LOCKED to the small transactions to avoid waiting for a long time.

Currently, I'm doing this with native queries, and it's a huge PITA.

@artworx commented on GitHub (Jun 2, 2021): In my use case, we have multiple short transactions waiting for a big batch. I need to add `SKIP LOCKED` to the small transactions to avoid waiting for a long time. Currently, I'm doing this with native queries, and it's a huge PITA.
Author
Owner

@adlpz commented on GitHub (Feb 22, 2024):

This appears to be live in DBAL's QueryBuilder. Are there any plans to mirror the functionality in ORM?

@adlpz commented on GitHub (Feb 22, 2024): This appears [to be live in DBAL's QueryBuilder](https://github.com/doctrine/dbal/pull/6191). Are there any plans to mirror the functionality in ORM?
Author
Owner

@greg0ire commented on GitHub (Feb 22, 2024):

@adlpz there is this comment

You can give it a try yourself if you want to speed things up.

@greg0ire commented on GitHub (Feb 22, 2024): @adlpz there is [this comment](https://github.com/doctrine/orm/pull/11061#issue-1995605196) You can give it a try yourself if you want to speed things up.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6251