Throttle email notifications upon updating document frequently (#4026)

* feat: add needed columns for throttling notifs

* feat: update model

* feat: deliver only one notif in a 12 hour window

* fix: address review comments

* prevent retry if notification update fails
* fix type compatibility instead of circumventing it
* add index for emailedAt

* fix: add metadata attr to EmailProps

* chore: decouple metadata from EmailProps

* chore: add test

* chore: revert sending metadata in props
This commit is contained in:
Apoorv Mishra
2022-09-07 16:51:30 +05:30
committed by GitHub
parent e4023d87e2
commit 1e39b564fe
7 changed files with 264 additions and 17 deletions

View File

@@ -1,15 +1,18 @@
import mailer from "@server/emails/mailer";
import Logger from "@server/logging/Logger";
import Metrics from "@server/logging/metrics";
import Notification from "@server/models/Notification";
import { taskQueue } from "@server/queues";
import { TaskPriority } from "@server/queues/tasks/BaseTask";
import { NotificationMetadata } from "@server/types";
interface EmailProps {
to: string;
}
export default abstract class BaseEmail<T extends EmailProps, S = any> {
export default abstract class BaseEmail<T extends EmailProps, S = unknown> {
private props: T;
private metadata?: NotificationMetadata;
/**
* Schedule this email type to be sent asyncronously by a worker.
@@ -17,7 +20,7 @@ export default abstract class BaseEmail<T extends EmailProps, S = any> {
* @param props Properties to be used in the email template
* @returns A promise that resolves once the email is placed on the task queue
*/
public static schedule<T>(props: T) {
public static schedule<T>(props: T, metadata?: NotificationMetadata) {
const templateName = this.name;
Metrics.increment("email.scheduled", {
@@ -31,6 +34,7 @@ export default abstract class BaseEmail<T extends EmailProps, S = any> {
name: "EmailTask",
props: {
templateName,
...metadata,
props,
},
},
@@ -45,8 +49,9 @@ export default abstract class BaseEmail<T extends EmailProps, S = any> {
);
}
constructor(props: T) {
constructor(props: T, metadata?: NotificationMetadata) {
this.props = props;
this.metadata = metadata;
}
/**
@@ -86,6 +91,23 @@ export default abstract class BaseEmail<T extends EmailProps, S = any> {
});
throw err;
}
if (this.metadata?.notificationId) {
try {
await Notification.update(
{
emailedAt: new Date(),
},
{
where: {
id: this.metadata.notificationId,
},
}
);
} catch (err) {
Logger.error(`Failed to update notification`, err, this.metadata);
}
}
}
/**