3

我正在使用一些耗时的计算来处理 PL/pgSQL 函数“interpolate_values”。名为“interpolation_jobs”的表包含有关每个函数调用的监视信息,因此具有给定 job_id 的函数调用的进度应该可以通过以下方式观察到

SELECT status FROM interpolation_jobs WHERE id = job_id;

“状态”列包含值“排队”、“运行”或“完成”之一。在函数开始时,状态从 'queued' 变为 'running',最后设置为 'done':

CREATE OR REPLACE FUNCTION interpolate_values (job_id INTEGER)
RETURNS VOID
LANGUAGE plpgsql VOLATILE
AS $$
DECLARE
BEGIN
    EXECUTE 'UPDATE interpolation_jobs
        SET status = ''running'', progress = 0.0
        WHERE id = ' || job_id || ';';

--
-- ... some extensive database computations ...
--

    EXECUTE 'UPDATE interpolation_jobs
        SET status = ''done''
        WHERE id = ' || job_id || ';';

END;
$$;

我的问题是在函数调用期间状态没有更新。更新实际上发生在函数调用返回时。因此,状态直接从“排队”变为“完成”。线条

    EXECUTE 'UPDATE interpolation_jobs
        SET status = ''running'', progress = 0.0
        WHERE id = ' || job_id || ';';

没有效果。

是否有可能立即更新 PL/pgSQL 中的值,以便在函数调用返回之前可以访问新值?

谢谢!

编辑:

感谢您的所有回答,这对我了解异步数据库操作的一般问题有很大帮助。dblink 方法对我有用。如果使用相同的数据库,则无需指定 IP/端口/用户:

SELECT * FROM current_database() INTO _db_name;
PERFORM dblink_connect('dbname=' || _db_name);
PERFORM dblink_exec('UPDATE interpolation_jobs SET status = ''running'' WHERE id =' || _job_id);

-- 
-- ... some extensive database computations ...
--

PERFORM dblink_exec('UPDATE interpolation_jobs SET status = ''done'' WHERE id =' || _job_id);
PERFORM dblink_disconnect();
4

2 回答 2

4

您似乎想要的是脏读或脏写。这些在 PostgreSQL 中不可用,并且可能永远不会被支持。

近亲是自主交易。同样,尽管添加它们的工作正在进行中,但 PostgreSQL 不支持这些。

您会发现,即使使用自治事务,也很难编写自己的高效排队系统。不要走这条路,使用某人已经写过的。排队系统很难写好,尤其是针对 RDBMS。ZeroMQ、RabbitMQ、 PGQ等现有系统可能非常值得作为替代品进行评估。

最近有关于添加一个特性的讨论,SELECT ... FOR UPDATE它可以让 PostgreSQL 跳过锁定的行并抓取第一行未锁定的行。这个功能目前还不可用,最快也要到 9.4 才能使用,所以不要屏住呼吸。

于 2013-04-29T11:47:56.503 回答
3

您可以使用dblink连接到您的数据库并执行将立即提交的查询:

CREATE OR REPLACE FUNCTION interpolate_values (_job_id INTEGER)
RETURNS VOID
LANGUAGE plpgsql VOLATILE
AS $$
DECLARE
_conn TEXT;
_status TEXT;
BEGIN
    _conn:='hostaddr=127.0.0.1 port=5433 dbname=<db> user=<user> password=<pass>';
    _status='running';
    PERFORM dblink_exec(_conn,'UPDATE interpolation_jobs SET status = '''||_status||''', progress = 0.0 WHERE id ='||_job_id);
    PERFORM pg_sleep(10); --simulate some time consuming calculations
    _status='finished';
    PERFORM dblink_exec(_conn,'UPDATE interpolation_jobs SET status = '''||_status||''', progress = 100.0 WHERE id ='||_job_id);
END;
$$;
于 2013-04-29T12:13:52.170 回答