From 1905c8bf7e49fcab07453210676843fd531afa41 Mon Sep 17 00:00:00 2001 From: Chris Smith <1979423+chris13524@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:16:13 -0500 Subject: [PATCH] fix: idempotency is per-client (#275) --- ...cations-primary-key-includes-client-id.sql | 8 +++++++ src/stores/notification.rs | 11 +++++----- tests/functional/stores/notification.rs | 22 +++++++++++++++---- 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 migrations/1699887339_notifications-primary-key-includes-client-id.sql diff --git a/migrations/1699887339_notifications-primary-key-includes-client-id.sql b/migrations/1699887339_notifications-primary-key-includes-client-id.sql new file mode 100644 index 00000000..217802a0 --- /dev/null +++ b/migrations/1699887339_notifications-primary-key-includes-client-id.sql @@ -0,0 +1,8 @@ +ALTER TABLE public.notifications + DROP CONSTRAINT notifications_pkey; + +ALTER TABLE public.notifications + ALTER COLUMN id SET NOT NULL; + +ALTER TABLE public.notifications + ADD PRIMARY KEY (id, client_id); diff --git a/src/stores/notification.rs b/src/stores/notification.rs index e876f4ec..0147d33c 100644 --- a/src/stores/notification.rs +++ b/src/stores/notification.rs @@ -48,11 +48,12 @@ impl NotificationStore for sqlx::PgPool { payload: &MessagePayload, ) -> stores::Result { let res = sqlx::query_as::( - "INSERT INTO public.notifications (id, tenant_id, client_id, last_payload) -VALUES ($1, $2, $3, $4) -ON CONFLICT (id) - DO UPDATE SET last_received_at = now() -RETURNING *;", + " + INSERT INTO public.notifications (id, tenant_id, client_id, last_payload) + VALUES ($1, $2, $3, $4) + ON CONFLICT (id, client_id) + DO UPDATE SET last_received_at = now() + RETURNING *;", ) .bind(id) .bind(tenant_id) diff --git a/tests/functional/stores/notification.rs b/tests/functional/stores/notification.rs index 45731954..4892c0b9 100644 --- a/tests/functional/stores/notification.rs +++ b/tests/functional/stores/notification.rs @@ -55,17 +55,31 @@ async fn notification_multiple_clients_same_payload(ctx: &mut StoreContext) { blob: "example-payload".to_string(), }; - let client_id = create_client(&ctx.clients).await; + let client_id1 = create_client(&ctx.clients).await; let res = ctx .notifications - .create_or_update_notification(&message_id, TENANT_ID, &client_id, &payload) + .create_or_update_notification(&message_id, TENANT_ID, &client_id1, &payload) .await; assert!(res.is_ok()); - let client_id = create_client(&ctx.clients).await; + let client_id2 = create_client(&ctx.clients).await; let res = ctx .notifications - .create_or_update_notification(&message_id, TENANT_ID, &client_id, &payload) + .create_or_update_notification(&message_id, TENANT_ID, &client_id2, &payload) .await; assert!(res.is_ok()); + + let notification1 = ctx + .notifications + .get_notification(&message_id, &client_id1, TENANT_ID) + .await + .unwrap(); + assert_eq!(notification1.client_id, client_id1); + + let notification2 = ctx + .notifications + .get_notification(&message_id, &client_id2, TENANT_ID) + .await + .unwrap(); + assert_eq!(notification2.client_id, client_id2); }