Skip to main content

Deployment

What it takes to run DB-backed email senders in an environment.

1. Database migration

Liquibase changeset 143-add-email-sender.sql creates the email_sender table (one row per SenderPurpose: BOOKING_CONFIRMATION, TRANSACTIONAL, MARKETING, INTERNAL_ALERT) and seeds it.

It runs automatically with the standard Liquibase migration on deploy (includeAll changelog), and it seeds the same non-prod (uat-) addresses in every environment, production included — the changelog has no liquibase contexts to branch on. Production is corrected by the UPDATEs below, run by hand.

2. Production cutover

Run these four statements in production only. No other environment should ever run them — QA and UAT are supposed to keep sending from the uat- addresses.

UPDATE email_sender SET from_address = 'auto-confirm@elivaas.com'  WHERE purpose = 'BOOKING_CONFIRMATION';
UPDATE email_sender SET from_address = 'notifications@elivaas.com' WHERE purpose = 'TRANSACTIONAL';
UPDATE email_sender SET from_address = 'news@elivaas.com' WHERE purpose = 'MARKETING';
UPDATE email_sender SET from_address = 'alerts@elivaas.com' WHERE purpose = 'INTERNAL_ALERT';

Connection details are in the prod-logs-and-db-access notes: bard-prod-db.postgres.database.azure.com/innsync, password in the bard-pms Container App secret spring-datasource-password.

SES needs no new identities: the elivaas.com domain identity is verified with sending enabled, so every address above can send already.

3. Consequence of skipping this step

This is not a one-address, cosmetic problem. EmailRequest.senderPurpose defaults to TRANSACTIONAL, and every caller that sends no explicit from now resolves through this table. So skipping (or forgetting) the cutover above moves all of production's outbound mail to the non-prod addresses at once:

  • Booking confirmations send from uat-auto-confirm@
  • Every OTP, payment receipt and cancellation notice sends from uat-notifications@
  • All bulk/marketing mail sends from uat-news@
  • Every staff alert sends from uat-alerts@

Guests who reply to a uat- address land wherever elivaas.com routes unmatched mail. Treat this as an estate-wide, guest-visible defect, not a single wrong sender.

The window isn't limited to a forgotten step, either: the migrator runs before the app starts, so there is always a brief, unavoidable interval — even on a clean deploy where the cutover is run promptly — where production sends from the uat- addresses.

4. The 60-second resolver cache

EmailSenderResolver caches the loaded rows for 60 seconds. A change made by the UPDATEs above is not instant — wait out the cache before verifying, and expect up to a minute of overlap where some sends still use the previous value.

5. Verify

After running the UPDATEs and waiting out the cache:

  1. Confirm a booking in production.
  2. Check the confirmation email's From: header — it should be auto-confirm@elivaas.com.
  3. Check the Reply-To: header — it should be reservations@elivaas.com.

6. Rollback

DELETE FROM email_sender;

With no rows, EmailSenderResolver falls back to the elivaas.notification.email.default-source property, returning behaviour to exactly what it was before this feature. No code rollback or redeploy is needed.