Skip to content

Added email analytics ingestion lag to Debug - #30685

Merged
jonatansberg merged 2 commits into
mainfrom
kevin-ber-3921-analytics-ingestion-lag
Sep 14, 2026
Merged

jonatansberg merged 2 commits into
mainfrom
kevin-ber-3921-analytics-ingestion-lag

Conversation

@kevinansfield

@kevinansfield kevinansfield commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

ref https://linear.app/ghost/issue/BER-3921/

Added server-calculated ingestion lag and UTC fetched-through timestamps to the analytics Debug overview, with separate delivery/failure, opens and recovery sections.

Progress advances through empty completed windows and uses the safe cursor when fetching hits an event limit. Failed fetches preserve previous progress. Unknown progress displays N/A, including on older backends. Scheduled refetches expose progress without wall-clock lag.

ref https://linear.app/ghost/issue/BER-3921/

Event timestamps alone cannot distinguish ingestion backlog from quiet periods.
Track completed fetch progress so Debug can show how far ingestion is behind.

- Expose fetchedThrough, lagSeconds and measuredAt for each analytics pipeline.
- Advance empty completed windows and use the safe cursor for capped fetches.
- Show separate delivery, opens and recovery lag with UTC progress timestamps.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-10T16:02:29.029044Z 69f017a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Essentials

Run ID: 17ef9eb7-acd3-4ecb-84d6-212a6c5227d8

📥 Commits

Reviewing files that changed from the base of the PR and between 69f017a and 1af32c8.

📒 Files selected for processing (4)
  • apps/ember-admin/app/components/posts/debug.hbs
  • apps/ember-admin/app/components/posts/debug.js
  • ghost/core/core/server/services/email-analytics/email-analytics-service.ts
  • ghost/core/test/unit/server/services/email-analytics/email-analytics-service.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (12)
  • GitHub Check: Acceptance tests (Node 22.23.1, mysql8)
  • GitHub Check: Acceptance tests (Node 24.20.0, mysql8)
  • GitHub Check: Unit tests (Node 22.23.1)
  • GitHub Check: Unit tests (Node 24.20.0)
  • GitHub Check: Legacy tests (Node 24.20.0, mysql8)
  • GitHub Check: Build Docker Images
  • GitHub Check: Legacy tests (Node 22.23.1, mysql8)
  • GitHub Check: App Playwright Acceptance Tests (@tryghost/admin)
  • GitHub Check: Lint
  • GitHub Check: Build Admin
  • GitHub Check: Admin tests - Chrome
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (7)
Review new or changed service boundaries for explicit dependency ownership, deterministic/idempotent initialisation, boot ordering, transaction and event semantics, cache coherence, and restart/multi-instance safety.

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/core/server/services/email-analytics/email-analytics-service.ts
Review whether tests prove changed behaviour, meaningful error/edge paths, and externally observable contracts without coupling to implementation details.

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/test/unit/server/services/email-analytics/email-analytics-service.test.ts
New source files must be TypeScript: flag new JS files as a required change unless exempt (DB migrations, apps/ember-admin/, tool/config files, scripts/, docker/, generated code).

⚙️ CodeRabbit configuration file

Files:

  • apps/ember-admin/app/components/posts/debug.js
Review lens: "where does this data become trusted?" Boundary data (HTTP input, external API/SDK responses, env/config, DB/filesystem reads, queue/webhook/event payloads) is `unknown` until validated — Zod by default.

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/test/unit/server/services/email-analytics/email-analytics-service.test.ts
  • ghost/core/core/server/services/email-analytics/email-analytics-service.ts
Prioritise concrete correctness, security, data-integrity, compatibility, and regression risks.

⚙️ CodeRabbit configuration file

Files:

  • apps/ember-admin/app/components/posts/debug.hbs
  • ghost/core/test/unit/server/services/email-analytics/email-analytics-service.test.ts
  • apps/ember-admin/app/components/posts/debug.js
  • ghost/core/core/server/services/email-analytics/email-analytics-service.ts
Type-safe boundaries: Fail only if the PR: consumes boundary data (HTTP input, external API/SDK responses, env/config, DB/filesystem reads, queue/webhook/event payloads) without validating it first — Zod by default, another format only wher...

📄 CodeRabbit inference engine (Custom checks)

Files:

  • ghost/core/test/unit/server/services/email-analytics/email-analytics-service.test.ts
  • ghost/core/core/server/services/email-analytics/email-analytics-service.ts
New files are TypeScript: Fail if the PR adds a new .js/.jsx/.cjs/.mjs source file, unless it is: a DB migration (ghost/core/core/server/data/migrations/), under apps/ember-admin/, a tool/config file, under scripts/ or docker/, or generated...

📄 CodeRabbit inference engine (Custom checks)

Files:

  • apps/ember-admin/app/components/posts/debug.js

Walkthrough

The email analytics service records successful fetch boundaries and exposes fetchedThrough and lagSeconds. Tests cover cursor handling, skipped and failed fetches, independent job progress, and missing-event recovery. The admin debug view formats and displays delivery, opens, and missing-event status details.

Suggested reviewers: evanhahn

Merge Risk: ⚪ Minimal · up to 1af32

This adds per-pipeline email analytics progress and ingestion-lag details to the Debug overview. No merge-blocking risk is evident.

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Type-Safe Boundaries ⚠️ Warning The PR adds unvalidated consumption of an HTTP response in apps/ember-admin/app/components/posts/debug.js. The response from ajax.request() is assigned and indexed directly. The new `fetchedThroug… Add a Zod schema at the ajax.request() boundary for the analytics status response, including the four status objects, optional/null date strings, and finite non-negative lagSeconds. Parse the response before assigning or indexing it. Us…
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
New Files Are Typescript ✅ Passed The pull-request diff adds no .js, .jsx, .cjs, or .mjs source file. It only modifies the pre-existing apps/ember-admin/app/components/posts/debug.js, which is explicitly exempt. The other ch…
Title check ✅ Passed The title clearly identifies the main change: adding email analytics ingestion lag to the Debug overview.
Description check ✅ Passed The description accurately covers the server-calculated ingestion lag, fetched-through timestamps, pipeline sections, progress handling, and scheduled refetch behavior.
Full details: Type-Safe Boundaries

Explanation

The PR adds unvalidated consumption of an HTTP response in apps/ember-admin/app/components/posts/debug.js. The response from ajax.request() is assigned and indexed directly. The new fetchedThrough value is passed to moment.utc() without schema or validity validation. No Zod schema or equivalent boundary parser exists in this path. The new lagSeconds check validates only that individual value, not the response shape. This file is changed for the feature, so the pre-existing-JS incidental exception does not apply. The TypeScript service changes use an internal injected callback and add no new typing bypass.

Resolution

Add a Zod schema at the ajax.request() boundary for the analytics status response, including the four status objects, optional/null date strings, and finite non-negative lagSeconds. Parse the response before assigning or indexing it. Use z.infer for the parsed status type, and handle parse failure with the existing unavailable-status behavior. Validate date strings with the schema or an explicit validity check before formatting them.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kevin-ber-3921-analytics-ingestion-lag

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 1af32c8

Command Status Duration Result
nx run @tryghost/admin:test:acceptance ✅ Succeeded 11m 58s View ↗
nx run ghost:test:ci:integration ✅ Succeeded 4m 40s View ↗
nx run ghost:test:integration ✅ Succeeded 3m 57s View ↗
nx run-many -t test:unit -p @tryghost/admin,ghost ✅ Succeeded 5m 19s View ↗
nx run ghost:test:ci:e2e ✅ Succeeded 4m 16s View ↗
nx run ghost:test:legacy ✅ Succeeded 3m 34s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 59s View ↗
nx run ghost-admin:test ✅ Succeeded 2m 42s View ↗
Additional runs (6) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-09-10 16:47:30 UTC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69f017a1ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +353 to +355
<tr>
<td>Fetched through:</td>
<td>{{ this.analyticsStatus.latest.fetchedThrough }}</td>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Implement the new diagnostics in React

This adds a new Admin feature directly to the legacy Ember debug screen, while repository guidance requires new Admin UI to be built in apps/admin using admin-x-framework and Shade. Move the ingestion-lag presentation and API consumption into the React Admin surface and include the required older-backend acceptance case rather than extending the Ember implementation.

AGENTS.md reference: AGENTS.md:L40-L44

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a follow-up in #30688 that migrates the debug screen

@codecov

codecov Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 35.71429% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.72%. Comparing base (2c5f376) to head (1af32c8).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
apps/ember-admin/app/components/posts/debug.js 0.00% 7 Missing ⚠️
...ervices/email-analytics/email-analytics-service.ts 71.42% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #30685      +/-   ##
==========================================
- Coverage   67.79%   67.72%   -0.08%     
==========================================
  Files        1670     1670              
  Lines       60326    60347      +21     
  Branches    10435    10441       +6     
==========================================
- Hits        40899    40867      -32     
- Misses      17123    17170      +47     
- Partials     2304     2310       +6     
Flag Coverage Δ
admin-tests 57.78% <0.00%> (-0.07%) ⬇️
e2e-tests 70.49% <71.42%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ref https://linear.app/ghost/issue/BER-3921/

Keep ingestion lag focused on ongoing pipelines and remove unused metadata and misleading captions.

- Exclude scheduled refetches from wall-clock lag and remove measuredAt.
- Remove the redundant cancellation guard and hard-coded buffer captions.
- Omit zero duration units, retaining 0s for zero lag.

@jonatansberg jonatansberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Two small, non-blocking things for your consideration:

  • The "Ingestion lag" row under Analytics Missing will always read ~30m on a healthy site, since that job's window ends at now - TRUST_THRESHOLD_MS. Might be worth relabelling or dropping the lag row there so it doesn't look like the job is behind?
  • scheduled.fetchedThrough is set on the backend and formatted in debug.js, but the scheduled block in the template doesn't render it yet. Show or drop?

@jonatansberg
jonatansberg merged commit 5f7ce21 into main Sep 14, 2026
58 checks passed
@jonatansberg
jonatansberg deleted the kevin-ber-3921-analytics-ingestion-lag branch September 14, 2026 13:02
jonatansberg added a commit that referenced this pull request Sep 14, 2026
ref https://linear.app/ghost/issue/BER-3911

#30685 added fetchedThrough and lagSeconds for the debug screen, which
measure the same thing as this branch's caughtUpTo. Keeping both would let
the lag logs, the metric and the debug screen disagree, so lag reporting
now derives from lagSeconds and the separate status shape is gone.
fetchedThrough also avoids two edge cases caughtUpTo had: a fetch skipped
because Mailgun isn't configured no longer counts as caught up, and a
failed first fetch after a restart no longer reports lag from a days-old
restored cursor.
jonatansberg added a commit that referenced this pull request Sep 15, 2026
ref https://linear.app/ghost/issue/BER-3911

The opened-events job warned when its cursor was older than
emailAnalytics:openedJobLagWarningMinutes. That cursor only moves when
events are processed, so quiet sites warned for days and an outage was
hard to tell apart from inactivity.

The job.completed log for the opened and delivery pipelines now carries
lag_seconds, read after each fetch from the ingestion lag getStatus()
already reports since #30685: the time since the end of the last
successful fetch window, or its safe cursor when a run was capped. It is
left out until a fetch has succeeded in the current process, so a restart
or an unconfigured Mailgun is reported neither as behind nor as caught
up. The threshold and its warning are removed; failed fetches already log
their own error. The missing-events sweep gets no lag entry because its
window trails the delivery pipeline by design.

The email debug screen keeps polling after a failed status request, and
GET /emails/:id/analytics has an e2e-api test.

Co-authored-by: Ricardo Pinto <hessian_loom_0u@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants