Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add broadcast functionality from triggers #1156

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

filipecabaco
Copy link
Contributor

@filipecabaco filipecabaco commented Sep 17, 2024

What kind of change does this PR introduce?

Adds a new functionality to broadcast db changes. It adds:

  • New function to broadcast from postgres using triggers

Example:

CREATE TABLE test_table (
    id serial PRIMARY KEY,
    name text NOT NULL,
    value integer NOT NULL,
    created_at timestamp DEFAULT CURRENT_TIMESTAMP
);

CREATE OR REPLACE FUNCTION broadcast_changes_for_test_table_trigger ()
    RETURNS TRIGGER
    AS $$
DECLARE
    topic text;
BEGIN
    topic = 'event:' || COALESCE(NEW.id::text, OLD.id::text);
    PERFORM
        realtime.broadcast_changes (topic, TG_OP, TG_OP, TG_TABLE_NAME, TG_TABLE_SCHEMA, NEW, OLD, TG_LEVEL);
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

CREATE TRIGGER broadcast_changes_for_test_table
    AFTER INSERT OR UPDATE OR DELETE ON test_table
    FOR EACH ROW
    EXECUTE FUNCTION broadcast_changes_for_test_table_trigger ();

-- Insert operation
INSERT INTO test_table (name, value) VALUES ('example_name', 42);

-- Update operation
UPDATE test_table SET value = 100 WHERE name = 'example_name';

-- Delete operation
DELETE FROM test_table WHERE name = 'example_name';

-- Send a message to the topic
SELECT realtime.send (to_jsonb ('{}'::text), 'test', 'test', FALSE);
   topic  | extension |     inserted_at     |     updated_at      |                                                                                                                                       payload                                                                                                                                       | event  | private |                  id                  
---------+-----------+---------------------+---------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------+--------------------------------------
 event:1 | broadcast | 2024-09-19 15:55:51 | 2024-09-19 15:55:51 | {"table": "test_table", "record": {"id": 1, "name": "example_name", "value": 42, "created_at": "2024-09-19T15:55:50.539754"}, "schema": "public", "operation": "INSERT", "old_record": null}                                                                                        | INSERT | t       | 39d6a1d8-04a4-4099-a2a3-e3c5a9b4605b
 event:1 | broadcast | 2024-09-19 15:55:53 | 2024-09-19 15:55:53 | {"table": "test_table", "record": {"id": 1, "name": "example_name", "value": 100, "created_at": "2024-09-19T15:55:50.539754"}, "schema": "public", "operation": "UPDATE", "old_record": {"id": 1, "name": "example_name", "value": 42, "created_at": "2024-09-19T15:55:50.539754"}} | UPDATE | t       | a34a5f73-13d2-4d11-b4ca-aaf041f8ad7a
 event:1 | broadcast | 2024-09-19 15:55:56 | 2024-09-19 15:55:56 | {"table": "test_table", "record": null, "schema": "public", "operation": "DELETE", "old_record": {"id": 1, "name": "example_name", "value": 100, "created_at": "2024-09-19T15:55:50.539754"}}                                                                                       | DELETE | t       | 6f5a291c-37b4-4051-96f4-98accdc5b201
 test    | broadcast | 2024-09-19 15:55:58 | 2024-09-19 15:55:58 | "{}"                                                                                                                                                                                                                                                                                | test   | f       | 6cc07f9e-4a59-42dd-9aa1-2d1b15e7827d

Copy link

vercel bot commented Sep 17, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
realtime-demo ⬜️ Ignored (Inspect) Visit Preview Sep 19, 2024 4:02pm

@filipecabaco filipecabaco force-pushed the feat/broadcast-messages-changes branch 3 times, most recently from b346ad0 to b27bf56 Compare September 17, 2024 20:16
@chasers
Copy link
Contributor

chasers commented Sep 17, 2024

Should our payload match what we have here in the Webhooks trigger?

@filipecabaco filipecabaco force-pushed the feat/broadcast-messages-changes branch 2 times, most recently from 9ab5171 to 0e27113 Compare September 17, 2024 21:15
Copy link

@olirice olirice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't hurt to have a check like

  IF TG_LEVEL = 'STATEMENT' THEN
    RAISE EXCEPTION 'realtime.broadcast_changes should be triggered for each row, not for each statement';
  END IF;

to make sure no one tries to "optimize" their trigger for bulk operations

row_data jsonb := '{}'::jsonb;
-- Declare entry that will be written to the realtime.messages table
topic_name text := TG_ARGV[0]::text;
event_name text := COALESCE(TG_ARGV[1]::text, TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the TG_ARGV[1]::text needed vs relying on TG_TABLE_SCHEMA + TG_TABLE_NAME?

it looks like that would allow users to override the event / qualified table name
what happens if the user overrides it with nonsense instead of a valid target?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our event names are arbitrary, this is just prescriptive for broadcasting changes from tables.

@filipecabaco we could just not make this an argument for now. One less thing for devs to think about.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea is to have this be flexible so if a user wants to have multiple tables to be funnelled into a single topic with a single event they are able to. just giving extra flexibility (and an extra gun to potentially lose a foot to be fair)

I'll bring this into the discussion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will keep the event overridable as it might make it easier for users to migrate to this approach

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our event names are arbitrary, this is just prescriptive for broadcasting changes from tables.

oh nice. In that case this looks like a great solution as long as its documented well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants