221 lines
7.3 KiB
PL/PgSQL
221 lines
7.3 KiB
PL/PgSQL
-- Batch membership mutations are one durable state transition. Row triggers
|
|
-- remain authoritative for ordinary statements, while the explicitly scoped
|
|
-- batch path suppresses their per-row write amplification and advances every
|
|
-- distinct dependency once before the transaction may commit.
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_membership_batch_active()
|
|
RETURNS boolean
|
|
LANGUAGE sql
|
|
STABLE
|
|
AS $$
|
|
SELECT COALESCE(current_setting('telesrv.membership_batch_mode', true), '') = 'on'
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_notify_channel_member_read_model()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
old_channel_id bigint;
|
|
old_user_id bigint;
|
|
new_channel_id bigint;
|
|
new_user_id bigint;
|
|
BEGIN
|
|
IF public.telesrv_membership_batch_active() THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
IF TG_OP <> 'INSERT' THEN
|
|
old_channel_id := OLD.channel_id;
|
|
old_user_id := OLD.user_id;
|
|
PERFORM public.telesrv_bump_read_model_version('channel_member', old_user_id, 'channel', old_channel_id);
|
|
PERFORM public.telesrv_bump_dialog_light(old_user_id, 'channel', old_channel_id);
|
|
END IF;
|
|
|
|
IF TG_OP <> 'DELETE' THEN
|
|
new_channel_id := NEW.channel_id;
|
|
new_user_id := NEW.user_id;
|
|
IF TG_OP = 'INSERT'
|
|
OR (new_user_id, new_channel_id) IS DISTINCT FROM (old_user_id, old_channel_id)
|
|
THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_member', new_user_id, 'channel', new_channel_id);
|
|
PERFORM public.telesrv_bump_dialog_light(new_user_id, 'channel', new_channel_id);
|
|
END IF;
|
|
END IF;
|
|
RETURN NULL;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_notify_channel_participants_read_model()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
old_channel_id bigint;
|
|
new_channel_id bigint;
|
|
BEGIN
|
|
IF public.telesrv_membership_batch_active() THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
IF TG_OP = 'DELETE' THEN
|
|
old_channel_id := OLD.channel_id;
|
|
ELSIF TG_OP = 'INSERT' THEN
|
|
new_channel_id := NEW.channel_id;
|
|
ELSE
|
|
old_channel_id := OLD.channel_id;
|
|
new_channel_id := NEW.channel_id;
|
|
END IF;
|
|
|
|
IF old_channel_id IS NOT NULL THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_participants', 0, 'channel', old_channel_id);
|
|
END IF;
|
|
IF new_channel_id IS NOT NULL AND new_channel_id IS DISTINCT FROM old_channel_id THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_participants', 0, 'channel', new_channel_id);
|
|
END IF;
|
|
RETURN NULL;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_notify_channel_dialog_light_read_model()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
old_owner_id bigint;
|
|
old_channel_id bigint;
|
|
new_owner_id bigint;
|
|
new_channel_id bigint;
|
|
BEGIN
|
|
IF public.telesrv_membership_batch_active() THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
IF TG_OP <> 'INSERT' THEN
|
|
old_owner_id := OLD.user_id;
|
|
old_channel_id := OLD.channel_id;
|
|
PERFORM public.telesrv_bump_dialog_light(old_owner_id, 'channel', old_channel_id);
|
|
END IF;
|
|
|
|
IF TG_OP <> 'DELETE' THEN
|
|
new_owner_id := NEW.user_id;
|
|
new_channel_id := NEW.channel_id;
|
|
IF TG_OP = 'INSERT'
|
|
OR (new_owner_id, new_channel_id) IS DISTINCT FROM (old_owner_id, old_channel_id)
|
|
THEN
|
|
PERFORM public.telesrv_bump_dialog_light(new_owner_id, 'channel', new_channel_id);
|
|
END IF;
|
|
END IF;
|
|
RETURN NULL;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_notify_channel_active_memberships_read_model()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
old_owner bigint;
|
|
new_owner bigint;
|
|
changed boolean;
|
|
BEGIN
|
|
IF public.telesrv_membership_batch_active() THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
IF TG_OP = 'DELETE' THEN
|
|
old_owner := OLD.user_id;
|
|
IF old_owner <> 0 THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_active_memberships', old_owner, 'user', old_owner);
|
|
END IF;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
new_owner := NEW.user_id;
|
|
IF TG_OP = 'INSERT' THEN
|
|
IF new_owner <> 0 THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_active_memberships', new_owner, 'user', new_owner);
|
|
END IF;
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
old_owner := OLD.user_id;
|
|
changed :=
|
|
OLD.user_id IS DISTINCT FROM NEW.user_id OR
|
|
OLD.channel_id IS DISTINCT FROM NEW.channel_id OR
|
|
OLD.status IS DISTINCT FROM NEW.status OR
|
|
OLD.deleted IS DISTINCT FROM NEW.deleted;
|
|
IF changed THEN
|
|
IF old_owner <> 0 THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_active_memberships', old_owner, 'user', old_owner);
|
|
END IF;
|
|
IF new_owner <> 0 AND new_owner IS DISTINCT FROM old_owner THEN
|
|
PERFORM public.telesrv_bump_read_model_version('channel_active_memberships', new_owner, 'user', new_owner);
|
|
END IF;
|
|
END IF;
|
|
RETURN NULL;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_bump_channel_membership_read_models(
|
|
p_channel_id bigint,
|
|
p_user_ids bigint[]
|
|
)
|
|
RETURNS void
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
changed record;
|
|
BEGIN
|
|
IF COALESCE(p_channel_id, 0) <= 0 OR COALESCE(cardinality(p_user_ids), 0) = 0 THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
FOR changed IN
|
|
WITH requested AS MATERIALIZED (
|
|
SELECT DISTINCT user_id
|
|
FROM unnest(p_user_ids) AS input(user_id)
|
|
WHERE user_id > 0
|
|
), keys AS MATERIALIZED (
|
|
SELECT 10 AS priority, 'channel_participants'::text AS model, 0::bigint AS owner_user_id,
|
|
'channel'::text AS peer_type, p_channel_id AS peer_id
|
|
UNION
|
|
SELECT 20, 'channel_member', user_id, 'channel', p_channel_id FROM requested
|
|
UNION
|
|
SELECT 30, 'dialog_light', user_id, 'channel', p_channel_id FROM requested
|
|
UNION
|
|
SELECT 40, 'dialog_owner', user_id, 'user', user_id FROM requested
|
|
UNION
|
|
SELECT 50, 'channel_active_memberships', user_id, 'user', user_id FROM requested
|
|
), bumped AS (
|
|
INSERT INTO public.read_model_versions (
|
|
model, owner_user_id, peer_type, peer_id, version, hash, updated_at
|
|
)
|
|
SELECT model, owner_user_id, peer_type, peer_id, 1,
|
|
public.telesrv_random_read_model_hash(), now()
|
|
FROM keys
|
|
ORDER BY priority, owner_user_id, peer_type, peer_id
|
|
ON CONFLICT (model, owner_user_id, peer_type, peer_id) DO UPDATE SET
|
|
version = public.read_model_versions.version + 1,
|
|
hash = public.telesrv_random_read_model_hash(),
|
|
updated_at = EXCLUDED.updated_at
|
|
RETURNING model, owner_user_id, peer_type, peer_id, version, hash
|
|
)
|
|
SELECT model, owner_user_id, peer_type, peer_id, version, hash
|
|
FROM bumped
|
|
ORDER BY model, owner_user_id, peer_type, peer_id
|
|
LOOP
|
|
PERFORM pg_notify(
|
|
'telesrv_read_model_changed',
|
|
json_build_object(
|
|
'model', changed.model,
|
|
'owner_user_id', changed.owner_user_id,
|
|
'peer_type', changed.peer_type,
|
|
'peer_id', changed.peer_id,
|
|
'version', changed.version,
|
|
'hash', changed.hash
|
|
)::text
|
|
);
|
|
END LOOP;
|
|
END;
|
|
$$;
|