9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
75 lines
3.9 KiB
PL/PgSQL
75 lines
3.9 KiB
PL/PgSQL
-- Replica-fence floor: make the ingest-time created_at envelope a commit-time
|
|
-- storage invariant for channel-window rows.
|
|
--
|
|
-- Why: cursor (keyset) pages may be served by a read replica behind a
|
|
-- "fence" timestamp. The fence proof requires that once a replica has
|
|
-- replayed past a sampled writer LSN, no transaction can later commit an
|
|
-- events row whose created_at is older than (commit time - floor). The
|
|
-- ingest handler checks |created_at - now| <= 900s at acceptance time, but
|
|
-- acceptance and commit are separated by unbounded async work, and several
|
|
-- writers (workflow sink, side effects, replace_*) bypass ingest entirely.
|
|
--
|
|
-- Mechanism: a DEFERRABLE INITIALLY DEFERRED constraint trigger runs inside
|
|
-- COMMIT processing, re-evaluating clock_timestamp() (NOT now(), which is
|
|
-- frozen at transaction start) so the bound is measured at commit, not at
|
|
-- INSERT. A transaction that holds an old-created_at insert open past the
|
|
-- floor budget is aborted at COMMIT and can never introduce a below-fence
|
|
-- row. Verified on PostgreSQL 16 against a partitioned table.
|
|
--
|
|
-- Scope: rows with channel_id IS NOT NULL — exactly the rows that channel
|
|
-- windows and thread pagination serve. channel_id-NULL rows (push leases,
|
|
-- profile/discovery snapshots) legitimately carry client-signed historical
|
|
-- timestamps and never appear in keyset-paged windows.
|
|
--
|
|
-- Enforcement is opt-in per session via the buzz.created_at_floor GUC
|
|
-- (seconds). The relay's writer pool sets it on every connection
|
|
-- (after_connect); when the GUC is unset or blank the guard is a no-op so
|
|
-- pg_restore/backfills and test fixtures that legitimately write historical
|
|
-- rows keep working. There is deliberately NO in-band bypass for
|
|
-- channel-bearing rows (the only structural exemption is channel_id IS
|
|
-- NULL, which never appears in keyset-paged windows): any operational
|
|
-- backfill of channel rows must run on a connection without the GUC — i.e.
|
|
-- outside the relay's writer pool — and the operator must hold the replica
|
|
-- breaker closed from before the backfill transaction begins until its WAL
|
|
-- is replayed on the replica (see Db::read routing docs). Disabling
|
|
-- triggers via session_replication_role = replica (pg_restore) is likewise
|
|
-- a breaker-closed operation.
|
|
--
|
|
-- Partition coverage: a constraint trigger created on the partitioned
|
|
-- parent is cloned onto every existing partition and onto partitions
|
|
-- created later (`CREATE TABLE .. PARTITION OF`), so partition rotation
|
|
-- keeps the guard. Row-level triggers also fire for COPY. Coverage across
|
|
-- the partition topology is asserted by a buzz-db test.
|
|
|
|
CREATE FUNCTION events_created_at_floor_guard() RETURNS trigger
|
|
LANGUAGE plpgsql AS $$
|
|
DECLARE
|
|
floor_secs numeric := nullif(current_setting('buzz.created_at_floor', true), '')::numeric;
|
|
BEGIN
|
|
IF floor_secs IS NOT NULL
|
|
AND floor_secs > 0
|
|
AND NEW.channel_id IS NOT NULL
|
|
AND NEW.created_at < clock_timestamp() - make_interval(secs => floor_secs)
|
|
THEN
|
|
RAISE EXCEPTION
|
|
'events.created_at % is more than % s before commit time %; below the replica-fence floor',
|
|
NEW.created_at, floor_secs, clock_timestamp()
|
|
USING ERRCODE = 'check_violation';
|
|
END IF;
|
|
RETURN NULL;
|
|
END
|
|
$$;
|
|
|
|
-- INSERT OR UPDATE OF: an UPDATE can move a previously exempt row into the
|
|
-- guarded set (channel_id NULL -> NOT NULL) or move a channel row's
|
|
-- created_at below the fence, so both mutation paths re-run the guard on the
|
|
-- NEW row. Partition-key note: a created_at rewrite that crosses partition
|
|
-- bounds is executed as DELETE + INSERT, which fires the cloned AFTER INSERT
|
|
-- guard on the destination partition; an in-partition rewrite fires the
|
|
-- UPDATE OF arm. Either way the NEW row is checked.
|
|
CREATE CONSTRAINT TRIGGER events_created_at_floor
|
|
AFTER INSERT OR UPDATE OF created_at, channel_id ON events
|
|
DEFERRABLE INITIALLY DEFERRED
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION events_created_at_floor_guard();
|