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

Is this function free from race-conditions?

我写了一个函数,它 return 给我一个处于 PENDING 状态的记录 ID(state 列)。并且它应该限制活动下载(处于“开始”或“完成”状态的记录)。因此,它可以帮助我避免资源限制问题。函数是:

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 个或更多线程将获得某些待处理记录或什至相同记录的 ID 和活动限制,即达到 STARTED/COMPLETED 下载)。 简而言之,这个函数应该作为测试和设置程序工作,并且 return 可用 ID(从 PENDING 切换到 STARTED,但这是无关紧要的细节)。它有这样的 属性 - 没有竞争条件吗?或者也许我必须使用一些锁...

PS。 1) downloads table 有列 idstate(一个枚举值,如 STARTED、PENDING、ERROR、COMPLETED、PROCESSED)和其他对问题的上下文。 2) max_run 是活动下载的限制。

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

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

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