这个函数没有竞争条件吗?

时间:2021-02-13 11:56:57

标签: postgresql plpgsql

我编写了一个函数,它返回一个处于 PENDING 状态的记录 ID(state 列)。它应该限制活动下载(处于 STARTED 或 COMPLETED 状态的记录)。因此,它可以帮助我避免资源限制问题。功能是:

CREATE FUNCTION start_download(max_run INTEGER) RETURNS INTEGER
LANGUAGE PLPGSQL AS
$$
DECLARE
  started_id INTEGER;
BEGIN

  UPDATE downloads SET state='STARTED' WHERE id IN (
    SELECT id FROM downloads WHERE state='PENDING' AND (
      SELECT max_run >= COUNT(id) FROM downloads WHERE state::TEXT IN ('STARTED','COMPLETED'))
  LIMIT 1 FOR UPDATE)
  RETURNING id INTO started_id;

RETURN started_id;
COMMIT;
END;
$$;

在竞争条件的意义上是安全的吗?我的意思是不会因为竞争条件而达到资源限制(2 个或更多线程将获得一些 PENDING 记录甚至相同记录的 ID 和活动限制,即将达到 STARTED/COMPLETED 下载)。 简而言之,这个函数应该作为测试和设置过程工作并返回可用的 ID(从 PENDING 切换到 STARTED,但它的细节无关紧要)。它是否具有这样的属性 - 没有竞争条件?或者也许我必须使用一些锁...

附注。 1) downloads 表包含列 idstate(一个枚举值,如 STARTED、PENDING、ERROR、COMPLETED、PROCESSED)和其他与问题上下文无关的列。 2) max_run 是活动下载的限制。

1 个答案:

答案 0 :(得分:2)

是的,最里面的子查询存在竞争条件。

如果两个并发会话同时运行你的函数,它们都会发现 max_run 等于已启动或已完成作业的数量,并且它们都会启动一个作业,从而推动启动的数量或运行超过限制的作业。

这不容易避免,除非您锁定所有已启动或已完成的作业(对并发性非常不利)或使用更高的事务隔离级别 (SERIALIZABLE)。

相关问题