mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
79 lines
3.2 KiB
PL/PgSQL
79 lines
3.2 KiB
PL/PgSQL
CREATE FUNCTION assert_community_write_allowed(target UUID) RETURNS VOID
|
|
LANGUAGE plpgsql AS $$
|
|
DECLARE
|
|
lifecycle TEXT;
|
|
generation BIGINT;
|
|
executor_community TEXT;
|
|
executor_generation TEXT;
|
|
serving_community TEXT;
|
|
serving_lease_id TEXT;
|
|
serving_owner TEXT;
|
|
serving_generation TEXT;
|
|
serving_fence_generation TEXT;
|
|
serving_lease_valid BOOLEAN := false;
|
|
BEGIN
|
|
-- The fence proof requires a fresh statement snapshot after lock grant;
|
|
-- pinned RR/Serializable snapshots can retain pre-fence authorization.
|
|
IF current_setting('transaction_isolation') <> 'read committed' THEN
|
|
RAISE EXCEPTION 'community writes require READ COMMITTED isolation'
|
|
USING ERRCODE = 'invalid_transaction_state';
|
|
END IF;
|
|
|
|
-- Nullable operator-attribution rows without a tenant are unrelated.
|
|
IF target IS NULL THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
PERFORM pg_advisory_xact_lock_shared(community_deletion_lock_key(target));
|
|
SELECT deletion_state, deletion_fence_generation
|
|
INTO lifecycle, generation
|
|
FROM communities
|
|
WHERE id = target;
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'community write rejected: community % is missing', target
|
|
USING ERRCODE = 'object_not_in_prerequisite_state';
|
|
END IF;
|
|
|
|
-- Authorization is evaluated independently for every community checked.
|
|
executor_community := current_setting('buzz.deletion_executor_community', true);
|
|
executor_generation := current_setting('buzz.deletion_fence_generation', true);
|
|
IF executor_community = target::TEXT
|
|
AND executor_generation ~ '^[0-9]+$'
|
|
AND executor_generation::BIGINT = generation THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- A serving mutation admitted before quiescing may finish only while its
|
|
-- exact durable lease remains current and bound to this fence generation.
|
|
serving_community := current_setting('buzz.serving_write_community', true);
|
|
serving_lease_id := current_setting('buzz.serving_write_lease_id', true);
|
|
serving_owner := current_setting('buzz.serving_write_owner', true);
|
|
serving_generation := current_setting('buzz.serving_write_generation', true);
|
|
serving_fence_generation := current_setting('buzz.serving_write_fence_generation', true);
|
|
IF lifecycle IN ('active', 'quiescing')
|
|
AND serving_community = target::TEXT
|
|
AND serving_lease_id ~ '^[0-9a-fA-F-]{36}$'
|
|
AND serving_generation ~ '^[0-9]+$'
|
|
AND serving_fence_generation ~ '^[0-9]+$'
|
|
AND serving_fence_generation::BIGINT = generation THEN
|
|
SELECT EXISTS(
|
|
SELECT 1 FROM community_serving_write_leases lease
|
|
WHERE lease.id = serving_lease_id::UUID
|
|
AND lease.community_id = target
|
|
AND lease.owner = serving_owner
|
|
AND lease.generation = serving_generation::BIGINT
|
|
AND lease.fence_generation = serving_fence_generation::BIGINT
|
|
AND lease.lease_until >= now()
|
|
) INTO serving_lease_valid;
|
|
IF serving_lease_valid THEN
|
|
RETURN;
|
|
END IF;
|
|
END IF;
|
|
|
|
IF lifecycle <> 'active' THEN
|
|
RAISE EXCEPTION 'community write fenced: community % generation %', target, generation
|
|
USING ERRCODE = 'object_not_in_prerequisite_state';
|
|
END IF;
|
|
END
|
|
$$;
|