-
Notifications
You must be signed in to change notification settings - Fork 924
feat: add notification deduplication trigger #14172
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
DROP TRIGGER IF EXISTS update_notification_message_dedupe_hash ON notification_messages; | ||
DROP FUNCTION IF EXISTS compute_notification_message_dedupe_hash(); | ||
ALTER TABLE IF EXISTS notification_messages | ||
DROP COLUMN IF EXISTS dedupe_hash; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
-- Add a column to store the hash. | ||
ALTER TABLE IF EXISTS notification_messages | ||
ADD COLUMN IF NOT EXISTS dedupe_hash TEXT NULL; | ||
|
||
COMMENT ON COLUMN notification_messages.dedupe_hash IS 'Auto-generated by insert/update trigger, used to prevent duplicate notifications from being enqueued on the same day'; | ||
|
||
-- Ensure that multiple notifications with identical hashes cannot be inserted into the table. | ||
CREATE UNIQUE INDEX ON notification_messages (dedupe_hash); | ||
|
||
-- Computes a hash from all unique messages fields and the current day; this will help prevent duplicate messages from being sent within the same day. | ||
-- It is possible that a message could be sent at 23:59:59 and again at 00:00:00, but this should be good enough for now. | ||
-- This could have been a unique index, but we cannot immutably create an index on a timestamp with a timezone. | ||
CREATE OR REPLACE FUNCTION compute_notification_message_dedupe_hash() RETURNS TRIGGER AS | ||
$$ | ||
BEGIN | ||
NEW.dedupe_hash := MD5(CONCAT_WS(':', | ||
NEW.notification_template_id, | ||
NEW.user_id, | ||
NEW.method, | ||
NEW.payload::text, | ||
ARRAY_TO_STRING(NEW.targets, ','), | ||
DATE_TRUNC('day', NEW.created_at AT TIME ZONE 'UTC')::text | ||
)); | ||
RETURN NEW; | ||
END; | ||
$$ LANGUAGE plpgsql; | ||
|
||
COMMENT ON FUNCTION compute_notification_message_dedupe_hash IS 'Computes a unique hash which will be used to prevent duplicate messages from being enqueued on the same day'; | ||
CREATE TRIGGER update_notification_message_dedupe_hash | ||
BEFORE INSERT OR UPDATE | ||
ON notification_messages | ||
FOR EACH ROW | ||
EXECUTE FUNCTION compute_notification_message_dedupe_hash(); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,8 @@ import ( | |
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/coder/quartz" | ||
|
||
"github.com/coder/serpent" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another instance of import grouping, this is the job of the tooling tbh (and it's failing), so up to you if you want to fix it 😄. (There are actually a few more of these too but I'll leave those uncommented.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll leave this for now; we need the tooling to be updated as you suggest. |
||
|
||
"github.com/coder/coder/v2/coderd/database" | ||
|
@@ -61,7 +63,7 @@ func TestMetrics(t *testing.T) { | |
method: handler, | ||
}) | ||
|
||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer")) | ||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) | ||
require.NoError(t, err) | ||
|
||
user := createSampleUser(t, store) | ||
|
@@ -228,7 +230,7 @@ func TestPendingUpdatesMetric(t *testing.T) { | |
method: handler, | ||
}) | ||
|
||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer")) | ||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) | ||
require.NoError(t, err) | ||
|
||
user := createSampleUser(t, store) | ||
|
@@ -305,7 +307,7 @@ func TestInflightDispatchesMetric(t *testing.T) { | |
method: delayer, | ||
}) | ||
|
||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer")) | ||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) | ||
require.NoError(t, err) | ||
|
||
user := createSampleUser(t, store) | ||
|
@@ -384,7 +386,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { | |
customMethod: webhookHandler, | ||
}) | ||
|
||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer")) | ||
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), quartz.NewReal()) | ||
require.NoError(t, err) | ||
|
||
user := createSampleUser(t, store) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added
created_at
to be passed in to make the dedupe hash testable by using Quartz to advance the clock.