BEFORE 触发器中的 CLOCK_TIMESTAMP 是否与 PG 12.3 中的 log/commit 顺序*完全*匹配?

Does CLOCK_TIMESTAMP from a BEFORE trigger match log/commit order *exactly* in PG 12.3?

我有一个 Postgres 12.3 问题:我能否依靠触发器中的 CLOCK_TIMESTAMP() 完全 相同的顺序标记 updated_dts 时间戳因为更改已提交给永久数据?

从表面上看,这听起来像是一个愚蠢的问题,但我只是花了两个时间在非 Postgres 系统中追踪一个非常罕见的竞争条件,它正是依赖于这种行为。 (延迟提交使他们的 'last value seen' 跟踪数据不可靠。)现在我试图弄清楚 CLOCK_TIMESTAMP() 是否有可能与 WAL 中记录的更改顺序不完全匹配。

很容易看出 NOW/TRANSACTION_TIMESTAMP/CURRENT_TIMESTAMP 是如何发生这种情况的,因为他们返回的是交易开始时间,而不是完成时间。在这种情况下,很容易记录时间戳序列,其中时间戳和日志顺序不一致。但我不知道是否有机会以与 BEFORE 触发器 CLOCK_TIMESTAMP() 值不同的顺序保存提交。

对于背景,我们需要一个 100% 可靠的时间表供外部搜索使用。据我了解,我可以使用逻辑复制和复制目标端触发器来创建一个,以便在从日志中重放更改时标记更改。我不清楚的是,是否有可能在单个服务器上从 CLOCK_TIMESTAMP() 获得相同的保真度。

我还没有深入了解 Postgres 内部结构的能力,也没有了解请求是如何交错的,也不知道执行的粒度如何,我希望这里有人确切地知道。如果这更像是一个 PG 邮件列表的问题,请告诉我。

-- 谢谢

下面是我如何构建时间戳的一些示例代码。它工作正常,但不能证明任何关于大量并发进程的行为。

---------------------------------------------
-- Create the trigger function
---------------------------------------------
DROP FUNCTION IF EXISTS api.set_updated CASCADE;

CREATE OR REPLACE FUNCTION api.set_updated()
RETURNS TRIGGER

AS $BODY$
BEGIN
   NEW.updated_dts = CLOCK_TIMESTAMP();
   RETURN NEW;
END;
$BODY$
language plpgsql;

COMMENT ON FUNCTION api.set_updated() IS 'Sets updated_dts field to CLOCK_TIMESTAMP(), if the record has changed..';

---------------------------------------------
-- Create the table
---------------------------------------------
DROP TABLE IF EXISTS api.numbers;

CREATE TABLE api.numbers (
    id          uuid NOT NULL DEFAULT extensions.gen_random_uuid (),
    number      integer NOT NULL DEFAULT NULL,
    updated_dts timestamptz NOT NULL DEFAULT 'epoch'::timestamptz
);

---------------------------------------------
-- Define the triggers (binding)
---------------------------------------------
-- NOTE: I'm guessing that in production that I can use DEFAULT CLOCK_TIMESTAMP() instead of a BEFORE INSERT trigger,
-- I'm using a distinct DEFAULT value, as I want it to pop out if I'm not getting the trigger to fire.
CREATE TRIGGER trigger_api_number_before_insert
    BEFORE INSERT ON api.numbers
    FOR EACH ROW
    EXECUTE PROCEDURE set_updated();

CREATE TRIGGER trigger_api_number_before_update
    BEFORE UPDATE ON api.numbers
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE PROCEDURE set_updated();

---------------------------------------------
-- INSERT some data
---------------------------------------------
INSERT INTO numbers (number) values (1),(2),(3);

---------------------------------------------
-- Take a look
---------------------------------------------
SELECT * from numbers ORDER BY updated_dts ASC; -- The values should be listed as 1, 2, 3 as oldest to newest.

---------------------------------------------
-- UPDATE a row
---------------------------------------------
UPDATE numbers SET number = 11 where number = 1;

---------------------------------------------
-- Take a look
---------------------------------------------
SELECT * from numbers ORDER BY updated_dts ASC; -- The values should be listed as 2, 3, 11 as oldest to newest.

不,您不能依赖 clock_timestamp() 触发器执行期间(或评估 DEFAULT 子句时)的顺序与提交顺序相同。

提交将始终晚于函数调用发生,并且您无法控制它们之间需要多长时间。

但我很惊讶这对你来说是个问题。通常,提交时间不可见或不相关。你为什么不简单地接受 clock_timestamp() 作为衡量事物的标准?