Skip to content

Commit

Permalink
Add Postgres notify trigger for route updates
Browse files Browse the repository at this point in the history
This adds triggers that send Postgres notifications for route_changes
when rows are inserted, updated and deleted for content items and
publish intents.
  • Loading branch information
theseanything committed Nov 6, 2024
1 parent 9b5984f commit 5ff28b0
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 2 deletions.
38 changes: 38 additions & 0 deletions db/migrate/20241105135438_add_notify_trigger_for_route_changes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class AddNotifyTriggerForRouteChanges < ActiveRecord::Migration[7.2]
def up
execute <<-SQL
CREATE OR REPLACE FUNCTION notify_route_change() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('route_changes', '');
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
SQL

execute <<-SQL
CREATE TRIGGER content_item_change_trigger
AFTER INSERT OR UPDATE OR DELETE ON content_items
FOR EACH ROW EXECUTE PROCEDURE notify_route_change();
SQL

execute <<-SQL
CREATE TRIGGER publish_intent_change_trigger
AFTER INSERT OR UPDATE OR DELETE ON publish_intents
FOR EACH ROW EXECUTE PROCEDURE notify_route_change();
SQL
end

def down
execute <<-SQL
DROP TRIGGER IF EXISTS content_item_change_trigger ON content_items;
SQL

execute <<-SQL
DROP TRIGGER IF EXISTS publish_intent_change_trigger ON publish_intents;
SQL

execute <<-SQL
DROP FUNCTION IF EXISTS notify_route_change();
SQL
end
end
3 changes: 1 addition & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_03_12_132747) do
ActiveRecord::Schema[7.2].define(version: 2024_11_05_135438) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
Expand Down Expand Up @@ -114,5 +114,4 @@
t.index ["uid"], name: "index_users_on_uid", unique: true
t.index ["updated_at"], name: "index_users_on_updated_at"
end

end
77 changes: 77 additions & 0 deletions spec/integration/notify_route_change_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require "rails_helper"

def postgres_listener(channel)
queue = Queue.new

Thread.new do
conn = ActiveRecord::Base.lease_connection
conn.execute("LISTEN #{channel}")
loop do
conn.raw_connection.wait_for_notify do |event, _id, _data|
queue << event
end
end
ensure
conn.execute "UNLISTEN #{channel}"
end

# Wait for the thread to be ready to listen for nofitications
sleep 0.1

queue
end

describe "Postgres trigger", type: :request, skip_db_cleaner: true do
before do
# We need to use truncation instead of transaction to ensure the changes are commited and the trigger is fired
DatabaseCleaner.clean
DatabaseCleaner.strategy = :truncation
end

it "sends a notification when content item created" do
listener = postgres_listener("route_changes")
create(:content_item)

expect(listener.pop).to eq("route_changes")
end

it "sends a notification when content item updated" do
content_item = create(:content_item)

listener = postgres_listener("route_changes")
content_item.update!(base_path: "/foo")

expect(listener.pop).to eq("route_changes")
end

it "sends a notification when content item destroyed" do
content_item = create(:content_item)
listener = postgres_listener("route_changes")
content_item.destroy!

expect(listener.pop).to eq("route_changes")
end

it "sends a notification when publish intent created" do
listener = postgres_listener("route_changes")
create(:publish_intent)

expect(listener.pop).to eq("route_changes")
end

it "sends a notification when publish intent updated" do
publish_intent = create(:publish_intent)
listener = postgres_listener("route_changes")
publish_intent.update!(publish_time: 10.minutes.from_now)

expect(listener.pop).to eq("route_changes")
end

it "sends a notification when publish intent destroyed" do
publish_intent = create(:publish_intent)
listener = postgres_listener("route_changes")
publish_intent.destroy!

expect(listener.pop).to eq("route_changes")
end
end

0 comments on commit 5ff28b0

Please sign in to comment.