Skip to content

Process and schedule gift email deliveries - #29851

Open
kevinansfield wants to merge 3 commits into
agent/ber-3851-delivery-emailfrom
agent/ber-3851-delivery-lifecycle
Open

Process and schedule gift email deliveries#29851
kevinansfield wants to merge 3 commits into
agent/ber-3851-delivery-emailfrom
agent/ber-3851-delivery-lifecycle

Conversation

@kevinansfield

Copy link
Copy Markdown
Member

Summary

  • claim due deliveries with an atomic pending-to-sending transition
  • persist transport acceptance as the authoritative sent fact
  • retry definite recoverable non-acceptance with exact one-shot wakeups
  • leave ambiguous handoffs in sending and report stale sending rows at startup
  • add the scheduler flush permission, startup recovery pass, and server endpoint
  • keep processing behind giftSubCustomization with no checkout producer yet

Stack

4 of 6. Stacked on #29850; provider outcome collection is next.

Issue: https://linear.app/ghost/issue/BER-3851/establish-immediate-email-delivery-for-gift-subscriptions

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 417dff3a-b2c1-4b8f-b6b4-c3fc5ba0084a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@kevinansfield
kevinansfield marked this pull request as ready for review August 10, 2026 15:11
@github-actions github-actions Bot added the migration [pull request] Includes migration for review label Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

It looks like this PR contains a migration 👀
Here's the checklist for reviewing migrations:

General requirements

  • ⚠️ Tested performance on staging database servers, as performance on local machines is not comparable to a production environment
  • Satisfies idempotency requirement (both up() and down())
  • Does not reference models
  • Filename is in the correct format (and correctly ordered)
  • Targets the next minor version
  • All code paths have appropriate log messages
  • Uses the correct utils
  • Contains a minimal changeset
  • Does not mix DDL/DML operations
  • Tested in MySQL and SQLite

Schema changes

  • Both schema change and related migration have been implemented
  • For index changes: has been performance tested for large tables
  • For new tables/columns: fields use the appropriate predefined field lengths
  • For new tables/columns: field names follow the appropriate conventions
  • Does not drop a non-alpha table outside of a major version

Data changes

  • Mass updates/inserts are batched appropriately
  • Does not loop over large tables/datasets
  • Defends against missing or invalid data
  • For settings updates: follows the appropriate guidelines

@kevinansfield
kevinansfield force-pushed the agent/ber-3851-delivery-lifecycle branch from fcd82ee to eb53f62 Compare August 10, 2026 15:12

@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: fcd82eee8a

ℹ️ 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".


try {
const key = await this.#internalKeys.get('ghost-scheduler');
this.#adapter.schedule(this.#buildJob(time.getTime(), key));

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 Preserve retries when one-shot scheduling fails

When obtaining the scheduler key or queueing the job fails, scheduleAt only logs the error and resolves; moreover, the scheduler adapter contract permits schedule() to return a rejecting promise, which this un-awaited call cannot catch. Because retryDelivery has already moved the gift to pending with a future delivery_attempt_at and there is no periodic delivery poll, a failed one-shot leaves that email undelivered until a restart, an unrelated flush, or another recovery pass. Surface the failure or arrange a fallback wake/poll so the persisted retry cannot be stranded.

Useful? React with 👍 / 👎.

Comment on lines +127 to +129
if (!labsService.isSet('giftSubCustomization')) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Re-arm deliveries skipped by the feature gate

If giftSubCustomization is disabled when a scheduled delivery callback fires, this subscriber returns while the endpoint still responds successfully, so the one-shot job is consumed without changing or re-scheduling the pending gift. Labs settings are updated at runtime, but re-enabling the flag does not call recoverAll; therefore a delivery that was already queued before a temporary disable remains pending until Ghost restarts or an unrelated flush occurs. Re-arm skipped work when the flag becomes enabled, or retain a periodic recovery path.

Useful? React with 👍 / 👎.

Comment on lines +280 to +282
const collection = await this.model.findAll({
filter: 'status:purchased+delivery_method:email+delivery_status:pending'
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Filter the delivery batch to rows that are due

For sites with many future scheduled gifts or delayed retries, every one-shot flush loads every pending email gift because this query has no deliver_at or delivery_attempt_at cutoff. processDeliveries then opens a claim transaction for each result, even though claimPendingDelivery rejects all but the due rows; since each pending gift also has its own scheduled flush, processing N future gifts performs roughly N² reads and claim attempts over their lifetime. Use a due-only query for batch processing while retaining a separate all-pending query for startup schedule recovery.

Useful? React with 👍 / 👎.

@nx-cloud

nx-cloud Bot commented Aug 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 ec484c7

Command Status Duration Result
nx run ghost:test:ci:integration ✅ Succeeded 2m 40s View ↗
nx run ghost:test:integration ✅ Succeeded 3m 6s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 40s View ↗
nx run ghost:test:legacy ✅ Succeeded 2m 57s View ↗
nx run-many -t test:unit -p ghost ✅ Succeeded 33s View ↗
nx run ghost-monorepo:lint:boundaries ✅ Succeeded 21s View ↗
nx run @tryghost/admin:build ✅ Succeeded 5s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded 1s View ↗
nx run-many -t lint -p ghost,ghost-monorepo ✅ Succeeded 21s View ↗

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


☁️ Nx Cloud last updated this comment at 2026-08-11 16:56:16 UTC

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.29513% with 271 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.37%. Comparing base (a020f7a) to head (ec484c7).

Files with missing lines Patch % Lines
...st/core/core/server/services/gifts/gift-service.ts 57.82% 148 Missing and 3 partials ⚠️
...e/server/services/gifts/gift-delivery-scheduler.ts 38.79% 71 Missing ⚠️
...rvices/gifts/gift-delivery-bookshelf-repository.ts 83.74% 33 Missing ⚠️
...core/server/services/gifts/gift-service-wrapper.js 69.04% 13 Missing ⚠️
...server/services/gifts/gift-bookshelf-repository.ts 80.00% 3 Missing ⚠️
Additional details and impacted files
@@                        Coverage Diff                        @@
##           agent/ber-3851-delivery-email   #29851      +/-   ##
=================================================================
+ Coverage                          75.32%   75.37%   +0.05%     
=================================================================
  Files                               1611     1614       +3     
  Lines                             141142   141889     +747     
  Branches                           17464    17583     +119     
=================================================================
+ Hits                              106311   106949     +638     
- Misses                             33753    33888     +135     
+ Partials                            1078     1052      -26     
Flag Coverage Δ
e2e-tests 77.49% <64.29%> (+0.04%) ⬆️

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.

@kevinansfield
kevinansfield force-pushed the agent/ber-3851-delivery-lifecycle branch from eb53f62 to e947423 Compare August 10, 2026 17:30
@kevinansfield
kevinansfield force-pushed the agent/ber-3851-delivery-lifecycle branch from e947423 to 451853d Compare August 11, 2026 08:43
@kevinansfield
kevinansfield force-pushed the agent/ber-3851-delivery-lifecycle branch from 99a0a28 to 0353712 Compare August 11, 2026 12:04
@kevinansfield
kevinansfield force-pushed the agent/ber-3851-delivery-lifecycle branch from 0353712 to f704014 Compare August 11, 2026 12:09
ref https://linear.app/ghost/issue/BER-3851/establish-immediate-email-delivery-for-gift-subscriptions
Atomic claims and exact one-shot wakeups make delivery durable and retryable without representing gifts as automations or introducing polling.
ref https://linear.app/ghost/issue/BER-3851/establish-immediate-email-delivery-for-gift-subscriptions

The lifecycle tests need to use the provider outcome error field introduced by the persistence layer rename.
ref https://linear.app/ghost/issue/BER-3851/establish-immediate-email-delivery-for-gift-subscriptions

Delivery attempts now operate through their own repository so scheduling, concurrency, retries, and cancellation no longer overload the gift redemption model.
@kevinansfield
kevinansfield force-pushed the agent/ber-3851-delivery-lifecycle branch from f704014 to ec484c7 Compare August 11, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

migration [pull request] Includes migration for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant