我编写了一个函数,它返回一个处于 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
表包含列 id
、state
(一个枚举值,如 STARTED、PENDING、ERROR、COMPLETED、PROCESSED)和其他与问题上下文无关的列。 2) max_run
是活动下载的限制。
答案 0 :(得分:2)
是的,最里面的子查询存在竞争条件。
如果两个并发会话同时运行你的函数,它们都会发现 max_run
等于已启动或已完成作业的数量,并且它们都会启动一个作业,从而推动启动的数量或运行超过限制的作业。
这不容易避免,除非您锁定所有已启动或已完成的作业(对并发性非常不利)或使用更高的事务隔离级别 (SERIALIZABLE
)。