Re: Serialization, Locking...implement processing Queue with a table - Mailing list pgsql-general

From D. Dante Lorenso
Subject Re: Serialization, Locking...implement processing Queue with a table
Date
Msg-id 08b801c319f4$7ff58fa0$1564a8c0@ROMULUS
Whole thread Raw
In response to Serialization, Locking...implement processing Queue with a table  ("D. Dante Lorenso" <dante@lorenso.com>)
List pgsql-general
Ah.  Very good, Manfred.  I see what you are saying now.
This will indeed work.  And it works without locking.
I like it.  Hmmm... So, to convert your SQL back into
pseudo-code of my own ... You are saying it works something
like this:

---------- 8< -------------------- 8< ----------
FUNCTION reserve_job
BEGIN
    SELECT and LOCK row_id matching our criteria. (SELECT FOR UPDATE)

    IF (row_id was found) THEN
        RESERVE row_id (UPDATE)
        RETURN (row_id) -- done
    ELSE
        Ask "Are you Sure?" there are no rows matching our criteria?
        IF (certainly no row_id exists) THEN
            RETURN (0) -- no row ID exists
        ELSE
            RETURN reserve_job -- recursive call
        END IF
    END IF
END

---------- 8< -------------------- 8< ----------

OK, I see this SHOULD INDEED work.  So, now my question is
about costs.  Assuming both approaches (test/retry VS lock/wait)
yield a functional result.  Which is best?  Which is most
efficient.  Here are some pros and cons:

LOCK/WAIT
--------------------------------
PROS
    - fewer queries made: only one lock and one select per call
    - easy to implement
    - access to function is granted in order requested (per
      PostgreSQL handling of locks - expectation is that no
      starvation should occur)
CONS
    - requires setting locks in application code
      which could be forgotten by app developers
    - locks are placed outside function blocking entire function
      which may have code that might be safely run concurrently

TEST/RETRY
--------------------------------
PROS
    - in theory, code SHOULD be entirely contained within a
      single stored procedure (less application coding needed)
    - no locks needed (faster execution?)
CONS
    - requires extra queries to determine empty queue
    - may starve if a single process continues to grab the same
      row as other processes
    - need recursive calls in PL/PGSQL?

I am going to try to implement your suggestion and see
what I get in comparison to what I am seeing now with my
LOCK/WAIT code.

Dante

D. Dante Lorenso
dante@direct2prospect.com
972-333-4139



> On Wed, 14 May 2003 01:10:39 -0500, "D. Dante Lorenso"
> <dante@lorenso.com> wrote:
> >> How do you distinguish between (i) and (ii)?  Just do
> >> SELECT job_id FROM job_table
> >>     WHERE processor_id = 0 LIMIT 1;
> >> If this returns 0 rows, you have (i).
> >> If it returns one row, you have (ii).
> >
> >You can't do it this way
>
> Oh.
>
> > because if this select IS successful,
> >you'll still need to LOCK the row for the update.
>
> That's exactly the reason why I told you ...
>
> |(ii)  The row has been reserved by another transaction running at the
> |same time.  In this case, restart at SELECT FOR UPDATE.
>                                    ^^
>                                  not after!
>
> If there is a row satisfying the WHERE clause but SELECT FOR UPDATE
> does not return it, this is the effect of a race condition:
>
> SELECT xmax,* FROM job;
>  xmax | id | pr
> ------+----+----
>     0 |  2 |  0
>     0 |  3 |  1
>     0 |  1 |  2
>     0 |  4 |  0
>
> Session 1                    Session 2
>
> BEGIN;
> SELECT xmax,* FROM job
>  WHERE pr = 0 FOR UPDATE LIMIT 1;
>  xmax | id | pr
> ------+----+----
>     0 |  2 |  0
> (1 row)
>
> select xmax,* FROM job
>  WHERE id = 2;
>   xmax | id | pr
> -------+----+----
>  58634 |  2 |  0
> (1 row)
>                              BEGIN;
>                              select xmax,* FROM job
>                               WHERE pr = 0 LIMIT 1;
>                               xmax  | id | pr
>                              -------+----+----
>                               58634 |  2 |  0
>                              (1 row)
>
>                              SELECT xmax,* FROM job
>                               WHERE pr = 0 FOR UPDATE LIMIT 1;
>                              -- waits, because the row with id=2
>                              -- satisfies the WHERE clause but
>                              -- is locked by transaction 58634 ...
> UPDATE job SET pr = 1
>  WHERE id = 2;
>                              -- The same would happen, if we did the
>                              -- SELECT FOR UPDATE here (after the
>                              -- UPDATE in the other session), because
>                              -- our *visible* version of the row
>                              -- still satisfies the WHERE clause.
>
> select xmax,* FROM job
>  WHERE id = 2;
>  xmax | id | pr
> ------+----+----
>     0 |  2 |  1
> (1 row)
> -- xmax = 0 because we see
> -- the newly inserted tuple
>
> COMMIT;
>                              -- continues ...
>                               xmax | id | pr
>                              ------+----+----
>                              (0 rows)
>                              -- because the row this SELECT was about
>                              -- to return does not satisfy pr = 0
>                              -- any more
>
>                              SELECT xmax,* FROM job
>                               WHERE pr = 0 LIMIT 1;
>                               xmax | id | pr
>                              ------+----+----
>                                  0 |  4 |  0
>                              (1 row)
>                              -- but there is another row, so ...
>
>                              SELECT xmax,* FROM job
>                               WHERE pr = 0 FOR UPDATE LIMIT 1;
>                               xmax | id | pr
>                              ------+----+----
>                                  0 |  4 |  0
>                              (1 row)
>                              -- does not necessarily return the same
>                              -- id as the previous SELECT
>
>                              UPDATE ...;
>                              COMMIT;
>
> HTH.
> Servus
>  Manfred
>


pgsql-general by date:

Previous
From: Bob Wheldon
Date:
Subject: AFAIK
Next
From: "Rob Larter"
Date:
Subject: query help