2017-04-10 5 views
2

Gibt es eine Möglichkeit, eine Abfrage mit PostgreSQL unter Verwendung SKIP LOCKED zu machen, um zu vermeiden, dass eine Zeile bereits gesperrt ist, während alles bereits gesperrt ist, um auf die erste unverriegelte Zeile zu warten?SPRUNG GESPERRT, wenn nicht alle Zeilen gesperrt sind

In meinem Anwendungsfall möchte ich vermeiden, falsches Negativ zu haben, wenn ich eine verfügbare Telefonnummer anfordere. Aber in dem Fall, wo ich die ganze Nummer gesperrt hätte, unwahrscheinlich, aber es könnte immer noch passieren und sollte umgangen werden.

SELECT plain_number FROM pool 
    ORDER BY RANDOM() 
    LIMIT 1 
    FOR UPDATE 
    SKIP LOCKED 
+0

Unrelated, aber: [ 'ORDER BY random()' wirklich schlecht] (http://StackOverflow.com/questions/8674718/best-way-to-select-random-rows-postgresql) – pozs

+0

Dieses * kann * lösbar sein mit [CTEs] (https://www.postgresql.org/docs/ current/static/queries-with.html), aber ich bin mir nicht sicher: Die [FOR UPDATE SKIP LOCKED] -Dokumente (https://www.postgresql.org/docs/current/static/sql-select.html # SQL-FOR-UPDATE-SHARE) nicht erwähnt sie explizit, nur die Sub-Selects werden erwähnt (also, syntaxweise sollte es auch in CTEs akzeptiert werden). – pozs

Antwort

2

Dies scheint zu funktionieren:

WITH sl AS (
    SELECT plain_number FROM pool ORDER BY random() LIMIT 1 FOR UPDATE SKIP LOCKED 
), 
fu AS (
    SELECT plain_number FROM pool WHERE NOT EXISTS(SELECT 1 FROM sl) ORDER BY random() LIMIT 1 FOR UPDATE 
) 
SELECT * FROM sl FULL JOIN fu USING (plain_number); 

Aber es wartet eine zufällige plain_number seine Verriegelung zu lösen. Ich glaube nicht, dass es möglich ist, auf die Freigabe der ersten Sperre zu warten.

Setup:

create table pool(
    plain_number text primary key 
); 

insert into pool(plain_number) 
select generate_series(1, 9)::text; 

Und hier ist ein wenig node.js Skript, das ich für den Test geschrieben:

const pg = require("pg"); 
const options = { 
    "user": "test", 
    "password": "test", 
    "database": "test" 
}; 

function thread_ish() { 
    const client = new pg.Client(options); 
    const end = client.end.bind(client); 
    const rollback = function (client) { 
     client.query("ROLLBACK", end); 
    }; 

    client.connect(function() { 
     client.query("BEGIN", function (err) { 
      if (err) { 
       console.error(err); 
       return rollback(client); 
      } 
      client.query(
       "WITH sl AS (" 
       + " SELECT plain_number FROM pool ORDER BY random() LIMIT 1 FOR UPDATE SKIP LOCKED" 
       + "), fu AS (" 
       + " SELECT plain_number FROM pool WHERE NOT EXISTS(SELECT 1 FROM sl) ORDER BY random() LIMIT 1 FOR UPDATE" 
       + ")" 
       + "SELECT * FROM sl FULL JOIN fu USING (plain_number)", 
       function (err, result) { 
        if (err) { 
         console.error(err); 
         return rollback(client); 
        } 

        console.log("Selected number is", result.rows); 
        setTimeout(function() { 
         client.query("COMMIT", end); 
        }, 1000); 
       } 
      ); 
     }); 
    }); 
} 

for (var i = 0; i < 13; ++i) { 
    setTimeout(thread_ish, Math.random() * 100); 
}