Postgresql锁定行以使用临时表进行更新

时间:2017-03-21 01:36:10

标签: postgresql

我遇到以下功能问题。此函数的目的是返回一组记录,如果在60秒内调用(几乎像队列),则不会再次返回这些记录。

当我一次运行这个时似乎工作正常,但是当我在我的线程应用程序中使用它时,我看到重复显示。我是否正确锁定了行?插入临时表时使用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
$$

1 个答案:

答案 0 :(得分:0)

您的函数应该锁定listing中的行,就像您在第一个出现的线程中一样。

第二个帖子的问题是这个子选择:

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

被行上的锁阻止,即使封闭的SELECT ... FOR UPDATE是。

因此,subselect将很高兴地看到之前的旧行版本第一个线程的UPDATE,然后在封闭的SELECT ... FOR UPDATE中阻塞,直到第一个线程完成。然后它继续再次更新相同的行。

我不确定是否将其视为错误 - 您可能想在pgsql-general邮件列表中询问。 最近CTE出现了类似的问题,请参阅this commit message fix this bug。有人可能会说这是一个类似的案例。

不幸的是,像你这样复杂的查询,我想不出比

更好的解决方案
LOCK TABLE listing IN EXCLUSIVE MODE;

在开始处理之前,这不是很令人满意。