Ghost/ghost/email-analytics-provider-mailgun/lib/EmailAnalyticsProviderMailgun.js
Steve Larson 4267ff9be6
Updated email analytics job to prioritize open events (#20800)
ref https://linear.app/tryghost/issue/ENG-1477
- updated email analytics job to prioritize open events
- put limits on non-open event fetching
- updated job to now restart itself until processing is at a
sufficiently low volume

Previously the EmailAnalytics job would process all event data equally.
When there's sufficient recipients (>20k), we could see delays in the
open rate data in Admin because of all the delivered events being
processed. Open events are far more important to users, so we've now
prioritized processing those events before any others.

Processing of events shouldn't be any faster or slower with this as this
doesn't change throughput, just order.

NOTE: Use the mailgun-mock-server in TryGhost/Toolbox for testing.
2024-08-20 17:25:01 +00:00

63 lines
2.4 KiB
JavaScript

const MailgunClient = require('@tryghost/mailgun-client');
const DEFAULT_EVENT_FILTER = 'delivered OR opened OR failed OR unsubscribed OR complained';
const PAGE_LIMIT = 300;
const DEFAULT_TAGS = ['bulk-email'];
class EmailAnalyticsProviderMailgun {
mailgunClient;
constructor({config, settings}) {
this.mailgunClient = new MailgunClient({config, settings});
this.tags = [...DEFAULT_TAGS];
if (config.get('bulkEmail:mailgun:tag')) {
this.tags.push(config.get('bulkEmail:mailgun:tag'));
}
}
/**
* Fetch from the last known timestamp-TRUST_THRESHOLD then work forwards
* through pages until we get a blank response. This lets us get events
* quicker than the TRUST_THRESHOLD
*
* @param {Function} batchHandler
* @param {Object} [options]
* @param {Number} [options.maxEvents] Not a strict maximum. We stop fetching after we reached the maximum AND received at least one event after begin (not equal) to prevent deadlocks.
* @param {Date} [options.begin]
* @param {Date} [options.end]
* @param {String[]} [options.events]
*/
fetchLatest(batchHandler, options) {
const mailgunOptions = {
limit: PAGE_LIMIT,
event: options?.events ? options.events.join(' OR ') : DEFAULT_EVENT_FILTER,
tags: this.tags.join(' AND '),
begin: options.begin ? options.begin.getTime() / 1000 : undefined,
end: options.end ? options.end.getTime() / 1000 : undefined,
ascending: 'yes'
};
return this.#fetchAnalytics(mailgunOptions, batchHandler, {
maxEvents: options.maxEvents
});
}
/**
* @param {Object} mailgunOptions
* @param {Number} mailgunOptions.limit
* @param {String} mailgunOptions.event
* @param {String} mailgunOptions.tags
* @param {String} [mailgunOptions.begin]
* @param {String} [mailgunOptions.ascending]
* @param {Function} batchHandler
* @param {Object} [options]
* @param {Number} [options.maxEvents] Not a strict maximum. We stop fetching after we reached the maximum AND received at least one event after begin (not equal) to prevent deadlocks.
*/
async #fetchAnalytics(mailgunOptions, batchHandler, options) {
return await this.mailgunClient.fetchEvents(mailgunOptions, batchHandler, options);
}
}
module.exports = EmailAnalyticsProviderMailgun;