-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added experimental background job queue (#20985)
ref https://linear.app/tryghost/issue/ENG-1556/ - added background job queue behind config flags - when enabled, is only used for the member email analytics updates in order to speed up the parent job, and take load off of the main process that is serving requests The intent here is to decouple certain code paths from the main process where it is unnecessary, or worse, where it's part of the request. Primary use cases are email analytics (particularly the member stats [open rate]) which are not particularly helpful in the period immediately following an email send, while the click traffic and delivered/opened events are. Related, the email link clicks themselves send off a cascade of events that are quite a burden on the main process currently and are somewhat tied to the request response when they needn't be. We'll be looking to tackle that after some initial testing with the email analytics job.
- Loading branch information
Showing
24 changed files
with
1,213 additions
and
81 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
.../core/server/data/migrations/versions/5.100/2024-10-31-15-27-42-add-jobs-queue-columns.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
const {combineNonTransactionalMigrations, createAddColumnMigration} = require('../../utils'); | ||
|
||
module.exports = combineNonTransactionalMigrations( | ||
createAddColumnMigration('jobs', 'metadata', { | ||
type: 'string', | ||
maxlength: 2000, | ||
nullable: true | ||
}), | ||
createAddColumnMigration('jobs', 'queue_entry', { | ||
type: 'integer', | ||
nullable: true, | ||
unsigned: true | ||
}) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
ghost/core/core/server/services/email-analytics/jobs/update-member-email-analytics/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
const queries = require('../../lib/queries'); | ||
|
||
/** | ||
* Updates email analytics for a specific member | ||
* | ||
* @param {Object} options - The options object | ||
* @param {string} options.memberId - The ID of the member to update analytics for | ||
* @returns {Promise<Object>} The result of the aggregation query (1/0) | ||
*/ | ||
module.exports = async function updateMemberEmailAnalytics({memberId}) { | ||
const result = await queries.aggregateMemberStats(memberId); | ||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
const assert = require('assert/strict'); | ||
const path = require('path'); | ||
const configUtils = require('../../utils/configUtils'); | ||
const models = require('../../../core/server/models'); | ||
const testUtils = require('../../utils/'); | ||
|
||
// Helper function to wait for job completion | ||
async function waitForJobCompletion(jobName, maxWaitTimeMs = 5000, checkIntervalMs = 50) { | ||
return new Promise((resolve, reject) => { | ||
const startTime = Date.now(); | ||
const intervalId = setInterval(async () => { | ||
if (Date.now() - startTime >= maxWaitTimeMs) { | ||
clearInterval(intervalId); | ||
reject(new Error(`Job ${jobName} did not complete within ${maxWaitTimeMs}ms`)); | ||
} | ||
const job = await models.Job.findOne({name: jobName}); | ||
if (!job) { | ||
clearInterval(intervalId); | ||
resolve(); | ||
} | ||
}, checkIntervalMs); | ||
}); | ||
} | ||
|
||
describe('Job Queue', function () { | ||
let jobService; | ||
before(testUtils.setup('default')); // this generates the tables in the db | ||
afterEach(async function () { | ||
await configUtils.restore(); | ||
}); | ||
|
||
describe('enabled by config', function () { | ||
beforeEach(async function () { | ||
configUtils.set('services:jobs:queue:enabled', true); | ||
jobService = require('../../../core/server/services/jobs/job-service'); | ||
}); | ||
|
||
it('should add and execute a job in the queue', async function () { | ||
this.timeout(10000); | ||
const job = { | ||
name: `add-random-numbers-${Date.now()}`, | ||
metadata: { | ||
job: path.resolve(__dirname, './test-job.js'), | ||
data: {} | ||
} | ||
}; | ||
|
||
// Add the job to the queue | ||
const result = await jobService.addQueuedJob(job); | ||
assert.ok(result); | ||
|
||
// Wait for the job to complete | ||
await waitForJobCompletion(job.name, 8000); // Increase wait time | ||
|
||
// Check job status | ||
const jobEntry = await models.Job.findOne({name: job.name}); | ||
|
||
// Verify that the job no longer exists in the queue | ||
assert.equal(jobEntry, null); | ||
}); | ||
}); | ||
|
||
describe('not enabled', function () { | ||
beforeEach(async function () { | ||
configUtils.set('services:jobs:queue:enabled', false); | ||
jobService = require('../../../core/server/services/jobs/job-service'); | ||
}); | ||
|
||
it('should not add a job to the queue when disabled', async function () { | ||
const job = { | ||
name: `add-random-numbers-${Date.now()}`, | ||
metadata: { | ||
job: path.resolve(__dirname, './test-job.js'), | ||
data: {} | ||
} | ||
}; | ||
|
||
// Attempt to add the job to the queue | ||
const result = await jobService.addQueuedJob(job); | ||
assert.equal(result, undefined); | ||
|
||
// Verify that the job doesn't exist in the queue | ||
const jobEntry = await models.Job.findOne({name: job.name}); | ||
assert.equal(jobEntry, null); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module.exports = function testJob() { | ||
const num1 = Math.floor(Math.random() * 100); | ||
const num2 = Math.floor(Math.random() * 100); | ||
const result = num1 + num2; | ||
|
||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.