javascript - E-postkøen fortsetter å sende ut duplikate e-poster


Rediger

Så jeg fant det ut, jeg overså følgende linje

                const emails = [...this.emailQueue];

                for (let i = 0; i < emails.length; i++) {
                        const [emailUuid, { attempts, emailLogEvent, ...emailConfig }] = emails[i];
 
+                       processEmails[emailUuid] = false;
                        emailClient(emailConfig)
                                .then((result) => {

Jeg prøver å implementere en e-postkø, men det fungerer ikke helt som forventet. Ideen er å svare fra et endepunkt så raskt som mulig og deretter bare la køadministratoren forsøke å behandle køen i bakgrunnen, i dette tilfellet sende ut en e-post.

Et av problemene er at intervallet fortsetter å kjøre når this.running er satt til true. Et annet problem er at en e-post noen ganger sendes ut flere ganger, når den bare skulle sendes ut én gang.

Jeg har også prøvd å bruke Redis for dette fordi jeg kjenner igjen lukkeproblemet med initialize og også så Bull som en potensiell løsning, men jeg prøver å unngå så mange unødvendige biblioteker som mulig. Ideelt sett vil jeg lage en slags baseklasse som jeg kan gjenbruke for ulike køer, for eksempel en SMS-kø, ikke bare en e-postkø. Men foreløpig antar jeg at jeg bare prøver å få denne e-postkøen til å fungere.

Hva gjør jeg feil her? Her er koden min:

import emailClient from 'services/emailClient';
import randomUuid from 'services/randomUuid'

export interface EmailClientConfig {
    to: string;
}

const MAX_ATTEMPTS_FOR_EMAILS = 5;
class EmailQueueManager {
    private running = false;
    private emailQueue = new Map();

    private handleRemoveSentEmailsFromQueue(sentEmailUuids: string[]) {
        // delete each sent out email from queue
        sentEmailUuids.forEach((sentEmailUuid) => {
            this.emailQueue.delete(sentEmailUuid);
        });

        // set state to "ready to process"
        this.running = false;
    }

    private async handleProcessEmailQueue() {
        // determines if queue is currently being executed
        this.running = true;
        const processedEmails: Record<string, boolean> = {};
        // list of successfully sent emails
        const sentEmailUuids: string[] = [];

        // do nothing if currently being executed, so as
        // to not send out duplicate emails
        if (!this.emailQueue.size) {
            this.running = false;
            return;
        }

        const emails = [...this.emailQueue];

        for (let i = 0; i < emails.length; i++) {
            const [emailUuid, { attempts, emailLogEvent, ...emailConfig }] = emails[i];

            emailClient(emailConfig)
                .then((result) => {
                    sentEmailUuids.push(emailUuid);

                    // maybe do something else
                })
                .catch((err) => {
                    if (attempts > MAX_ATTEMPTS_FOR_EMAILS) {
                        // remove from queue permanently if
                        // attempt count exceeds what is allowed
                        this.emailQueue.delete(emailUuid);
                    } else {
                        // send back to queue and attempt to send email out again
                        this.emailQueue.set(emailUuid, {
                            attempts: attempts + 1,
                            emailLogEvent,
                            ...emailConfig,
                        });
                    }
                })
                .finally(() => {
                    // flag email as "processed", regardless of whether
                    // or not is was successfully sent
                    processedEmails[emailUuid] = true;
                });
        }

        // check every 250ms if all emails in queue
        // were processed (not necessarily successfully sent out)
        const checkProcessedEmailsInterval = setInterval(() => {
            if (Object.values(processedEmails).every(Boolean)) {
                this.handleRemoveSentEmailsFromQueue(sentEmailUuids);
                clearInterval(checkProcessedEmailsInterval);
            }
        }, 250);
    }
    public addEmailToQueue({
        emailConfig,
        emailLogEvent,
    }: {
        emailConfig: EmailClientConfig;
        emailLogEvent: string;
    }) {
        // add email to queue
        this.emailQueue.set(randomUuid(), { attempts: 0, emailLogEvent, ...emailConfig });
        // and immediately process the queue
        this.handleProcessEmailQueue();
    }

    public initialize() {
        // check every minute if there are
        // emails in queue that need to be processed
        setInterval(() => {
            if (!this.running) {
                this.handleProcessEmailQueue();
            }
        }, 1000 * 60);
    }
}

const emailQueueManager = new EmailQueueManager();

// instantiate the interval
emailQueueManager.initialize();

export default emailQueueManager;

Og jeg bruker dette slik:

import emailQueueManager from 'src/managers/queues/emailQueue';

. . .
async function someEndpoint(req, res) {
    // do stuff
    emailQueueManager.addEmailToQueue({
        emailConfig: {
            to: '[email ',
        },
        emailLogEvent: 'sent_email',
    });
    // do more stuff

    res.end()
}
. . .

Relaterte artikler