From 3b4fbcf4b3216b38d3a9c4b73b52e397ac01775a Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Tue, 11 Aug 2026 15:54:03 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Improved=20reliability=20of=20em?= =?UTF-8?q?ail=20analytics=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://github.com/TryGhost/Ghost/pull/29890#pullrequestreview-4910616569 Big picture ----------- We sometimes failed to create the job row for the "missing" job. This could cause: - unnecessary extra fetches from Mailgun - events to be dropped entirely, if the site was off for awhile Details ------- There are four jobs for email analytics: 1. opened 2. non-opened 3. scheduled 4. missing Before this change, `setJobTimestamp` failed to upsert the job row. For the first three job types, it turned out that was fine, because they were already created by previous code paths. But not for the "missing" job! Before this change, we'd only create the "missing" job row when: - no missing events were found - there was an error processing the batch Now, we create it at the right time as intended. --- .../server/services/email-analytics/lib/queries.ts | 1 + .../server/services/email-analytics/queries.test.ts | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/ghost/core/core/server/services/email-analytics/lib/queries.ts b/ghost/core/core/server/services/email-analytics/lib/queries.ts index 92a52233594..e155fcf144e 100644 --- a/ghost/core/core/server/services/email-analytics/lib/queries.ts +++ b/ghost/core/core/server/services/email-analytics/lib/queries.ts @@ -175,6 +175,7 @@ export class Queries { id: new ObjectID().toHexString(), name: jobName, [updateField]: date.toISOString(), // force to iso string for sqlite + created_at: date.toISOString(), // force to iso string for sqlite updated_at: date.toISOString(), // force to iso string for sqlite status: status }); diff --git a/ghost/core/test/unit/server/services/email-analytics/queries.test.ts b/ghost/core/test/unit/server/services/email-analytics/queries.test.ts index 6c50e360a27..c7e894f755e 100644 --- a/ghost/core/test/unit/server/services/email-analytics/queries.test.ts +++ b/ghost/core/test/unit/server/services/email-analytics/queries.test.ts @@ -269,6 +269,17 @@ describe('Email analytics queries', function () { assert.equal(job.status, 'finished'); }); + it('creates missing job with created timestamp', async function () { + const date = new Date('2026-08-11T10:00:00.000Z'); + + await queries.setJobTimestamp('email-analytics-missing', 'started', date); + + const job = await knex('jobs').where('name', 'email-analytics-missing').first(); + assert.equal(new Date(job.created_at).toISOString(), date.toISOString()); + assert.equal(new Date(job.started_at).toISOString(), date.toISOString()); + assert.equal(new Date(job.updated_at).toISOString(), date.toISOString()); + }); + it('swallows database errors', async function () { await knex.schema.dropTable('jobs');