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()
作为衡量事物的标准?
我有一个 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()
作为衡量事物的标准?