2017-03-21 12 views
0

次の機能に問題があります。この関数の目的は、60秒以内に呼び出された場合に再び返されることのないレコードのセットを返すことです(ほぼキューのように)。一時テーブルを使用した更新のPostgreSQLロック行

これを実行するとうまくいくようですが、スレッドアプリケーションで使用しているときに重複が表示されます。行を正しくロックしていますか?一時テーブルに挿入するときにFOR UPDATEを使用する正しい方法は何ですか?

CREATE OR REPLACE FUNCTION needs_quantities(computer TEXT) 
    RETURNS TABLE(id BIGINT, listing_id CHARACTER VARYING, asin CHARACTER VARYING, retry_count INT) 
LANGUAGE plpgsql 
AS $$ 

BEGIN 


    CREATE TEMP TABLE temp_needs_quantity ON COMMIT DROP 
    AS 

    SELECT 
     listing.id, 
     listing.listing_id, 
     listing.asin, 
     listing.retry_count 
    FROM listing 
    WHERE listing.id IN (
     SELECT min(listing.id) AS id 
     FROM listing 
     WHERE (listing.quantity_assigned_to IS NULL 

      --quantity is null 
      -- and quantity assigned date is at least 60 seconds ago 
      -- and quantity date is within 2 hours 

      OR (
       quantity IS NULL AND listing.quantity_assigned_date < now_utc() - INTERVAL '60 second' 
       AND (listing.quantity_date IS NULL OR listing.quantity_date > now_utc() - INTERVAL '2 hour') 
      ) 

      ) 
      AND listing.retry_count < 10 


     GROUP BY listing.asin 
     ORDER BY min(listing.retry_count), min(listing_date) 
     LIMIT 10 

    ) 
    FOR UPDATE; 


    UPDATE listing 
    SET quantity_assigned_date = now_utc(), quantity_assigned_to = computer 
    WHERE listing.id IN (SELECT temp_needs_quantity.id 
         FROM temp_needs_quantity); 

    RETURN QUERY 
    SELECT * 
    FROM temp_needs_quantity 
    ORDER BY id; 


END 
$$ 

答えて

0

あなたの関数は、付属の最初のスレッドと同じように、行をlistingにロックする必要があります。

... 
WHERE listing.id IN (
    SELECT min(listing.id) AS id 
    FROM listing 
    ... 
    LIMIT 10 
) 

封入SELECT ... FOR UPDATEであっても行のロックによってブロック、ない:

第二のスレッドにおける問題は、この副選択をすることです。最初のスレッドが完了するまで

だから副選択は喜んで囲むSELECT ... FOR UPDATEで、その後、最初のスレッドの
UPDATE前にブロックを から古いバージョンの行が表示されます。その後、同じ行を再度更新します。

これはバグと見なされるかどうかわかりません。– pgsql-generalメーリングリストにお問い合わせください。 最近CTEと同様の問題がありました。this commit message固定this bugを参照してください。これは同様のケースであると主張することができる。あなたは非常に満足のいくものではない処理を、開始する前に、私は

LOCK TABLE listing IN EXCLUSIVE MODE; 

へのよりよい解決策を考えることができないあなたのような複雑なクエリで残念ながら

関連する問題