Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Select the first row in a group but in concurrent environment

I have multiple processes of the same application running at the same time as each other. Each of these processes performs some computations.

I have a "tests" table that looks like this:

 id | a | b |      c
----+---+---+-------------
 1  | a | b | Helloooo!!!
 2  | a | b | Helloooo...
 3  | c | d | World!!!
 4  | c | d | World...

Each process needs to retrieve the first row in a group (group by columns "a" and "b") from the table "tests" to perform some computations over it but one process shouldn’t be able to select the row that has been already captured by another process. For example:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

 id | a | b |      c
----+---+---+-------------
 1  | a | b | Helloooo!!!                  <------ Captured by process #1 and it won't have been available until the transaction is finished by process #1.
 2  | a | b | Helloooo...                  <------ Unavailable.
 3  | c | d | World!!!                     <------ Should be captured by concurrent process #2.
 4  | c | d | World...                     <------ Unavailable.

My SQL query without row-level locks:

WITH t AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY a, b ORDER BY id ASC) AS rn
           FROM tests)
SELECT *
FROM t
WHERE rn = 1;

I tried to do something like this:

WITH groups AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY a, b ORDER BY id ASC) AS rn FROM tests)
SELECT * FROM (SELECT * FROM groups WHERE rn = 1) subquery FOR UPDATE SKIP LOCKED LIMIT 1;

But both of the processes retrieves the same row (transactions are opened and aren’t committed):

Process #1

  • Process #1.

Process #2

  • Process #2.

>Solution :

It might not be clear to the planner which rows of which actual table table you’re trying to lock.

This seems to work:

BEGIN TRANSACTION;

WITH groups AS (
    SELECT  id, 
            ROW_NUMBER() OVER w1 AS rn 
    FROM tests
    window w1 as (PARTITION BY a, b ORDER BY id ASC))
SELECT * 
FROM tests inner join groups using (id) WHERE groups.rn = 1
FOR UPDATE SKIP LOCKED LIMIT 1;

That being said, I’m still trying to work out what exactly in your initial query prevented the expected locking behaviour.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading