From d675278b0b5ffedcc5f971ea6921ff6d07e26ebc Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Wed, 2 Dec 2020 08:17:44 +0000 Subject: [PATCH] Prevented scheduling of recurring analytics jobs when not using emails (#12441) no issue - recurring jobs spin up worker threads which can be quite CPU intensive even when not performing much processing, this can be problematic in environments where there are many Ghost instances running - updated the email job scheduling to be skipped on bootup when there are no emails in the database and to be started when the first email is created as long as we're not in testing env - increase analytics job schedule from every 2 minutes to every 5 minutes to help spread the load further across instances --- core/server/index.js | 18 ++------- .../services/email-analytics/jobs/index.js | 38 +++++++++++++++++++ core/server/services/mega/mega.js | 4 ++ 3 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 core/server/services/email-analytics/jobs/index.js diff --git a/core/server/index.js b/core/server/index.js index f09c77e34d..a414b23027 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -10,7 +10,6 @@ require('./overrides'); const debug = require('ghost-ignition').debug('boot:init'); -const path = require('path'); const Promise = require('bluebird'); const config = require('../shared/config'); const {events, i18n} = require('./lib/common'); @@ -70,26 +69,15 @@ function initialiseServices() { }); } -function initializeRecurringJobs() { +async function initializeRecurringJobs() { // we don't want to kick off scheduled/recurring jobs that will interfere with tests if (process.env.NODE_ENV.match(/^testing/)) { return; } - const jobsService = require('./services/jobs'); - if (config.get('backgroundJobs:emailAnalytics')) { - // use a random seconds value to avoid spikes to external APIs on the minute - const s = Math.floor(Math.random() * 60); // 0-59 - // run every 2 minutes, either on 1,3,5... or 2,4,6... - const m = Math.floor(Math.random() * 2) + 1; // 1-2 - - jobsService.scheduleJob( - `${s} ${m}/2 * * * *`, - path.resolve(__dirname, 'services', 'email-analytics', 'jobs', 'fetch-latest.js'), - undefined, - 'email-analytics-fetch-latest' - ); + const emailAnalyticsJobs = require('./services/email-analytics/jobs'); + await emailAnalyticsJobs.scheduleRecurringJobs(); } } diff --git a/core/server/services/email-analytics/jobs/index.js b/core/server/services/email-analytics/jobs/index.js new file mode 100644 index 0000000000..583039a70c --- /dev/null +++ b/core/server/services/email-analytics/jobs/index.js @@ -0,0 +1,38 @@ +const path = require('path'); +const config = require('../../../../shared/config'); +const models = require('../../../models'); +const jobsService = require('../../jobs'); + +let hasScheduled = false; + +module.exports = { + async scheduleRecurringJobs() { + if ( + !hasScheduled && + config.get('backgroundJobs:emailAnalytics') && + !process.env.NODE_ENV.match(/^testing/) + ) { + // don't register email analytics job if we have no emails, + // processer usage from many sites spinning up threads can be high + const emailCount = await models.Email.count(); + + if (emailCount > 0) { + // use a random seconds value to avoid spikes to external APIs on the minute + const s = Math.floor(Math.random() * 60); // 0-59 + // run every 5 minutes, on 1,6,11..., 2,7,12..., 3,8,13..., etc + const m = Math.floor(Math.random() * 5); // 0-4 + + jobsService.scheduleJob( + `${s} ${m}/5 * * * *`, + path.resolve(__dirname, 'fetch-latest.js'), + undefined, + 'email-analytics-fetch-latest' + ); + + hasScheduled = true; + } + } + + return hasScheduled; + } +}; diff --git a/core/server/services/mega/mega.js b/core/server/services/mega/mega.js index b905283367..670ea29acb 100644 --- a/core/server/services/mega/mega.js +++ b/core/server/services/mega/mega.js @@ -220,6 +220,10 @@ async function pendingEmailHandler(emailModel, options) { return; } + // make sure recurring background analytics jobs are running once we have emails + const emailAnalyticsJobs = require('../email-analytics/jobs'); + emailAnalyticsJobs.scheduleRecurringJobs(); + return jobService.addJob(sendEmailJob, {emailModel}); }