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:
- Confirm a booking in production.
- Check the confirmation email's
From:header — it should beauto-confirm@elivaas.com. - Check the
Reply-To:header — it should bereservations@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.