Skip to content

[Team Deletions] deletion e-mails - #6624

Open
aerosol wants to merge 6 commits into
team-sites-deletions-schedule-fsmfrom
team-sites-deletions-emails
Open

[Team Deletions] deletion e-mails#6624
aerosol wants to merge 6 commits into
team-sites-deletions-schedule-fsmfrom
team-sites-deletions-emails

Conversation

@aerosol

@aerosol aerosol commented Aug 27, 2026

Copy link
Copy Markdown
Member

Changes

This PR introduces two new emails: deletion full notice (T-30d, with trial/subscription branching and capped site list) and deletion reminder (T-5d). Each subscription is re-cehcekd before sending to address the reactivation race.

AcceptTrafficUntil worker's "stats stop tomorrow" e-mail now includes the pending deletion date for steady-state (non-backlog) expired trials, instead of separate e-mail.

The worker is disabled for now.

@metmarkosaric if you could have a final look at the copy please

@aerosol
aerosol force-pushed the team-sites-deletions-emails branch from f7cc2ed to 5d3e926 Compare August 27, 2026 05:32
@aerosol aerosol changed the title wip deletion emails [Team Deletions] deletion e-mails Aug 31, 2026
@metmarkosaric

Copy link
Copy Markdown
Contributor

@aerosol thanks, the copy looks good!

@aerosol
aerosol requested a review from zoldar September 1, 2026 08:41
Comment thread lib/plausible/team_deletion_schedules.ex
from(sch in TeamDeletionSchedule,
where: sch.status == :scheduled,
where: sch.first_notice_due_date <= ^today,
preload: [team: [:owners, :billing_members]]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's a very minor improvement but at least team preload could be done from a join.

from(sch in TeamDeletionSchedule,
where: sch.status == :first_notice_sent,
where: sch.deletion_date <= ^reminder_threshold,
preload: [team: [:owners, :billing_members]]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ditto about preloading team from a join.


pending_trial_schedules_by_team_id =
notifications
|> Enum.filter(&(&1.deadline == tomorrow))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Even though we don't do any fancy stuff like mixing calendars, I'd opt for using Date.compare instead of struct comparison, just to be safe.

Comment thread lib/plausible/team_deletion_schedules.ex
pending_trial_schedules_by_team_id
|> Map.values()
|> Enum.each(fn schedule ->
TeamDeletionSchedules.mark_first_notice_sent(schedule, report_if_invalid?: true)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not run it directly after sending the notification in the original loop? 🤔

@aerosol aerosol Sep 2, 2026

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.

Poor man's fault tolerance. Correct me if I'm wrong: first, we send out a bunch of notifications and store "send_accept_traffic_until_notifications" rows for each, in the same loop, to minimize the risk of sending dupe e-mails in case the job runs again.

Then in another loop, we'll mark deletions schedules as "first notice sent" so deletion schedules can advance. If any of those DB updates fails, the worker gets restarted - it won't send first notice e-mails again, but it will advance the deletion schedules that weren't touched due to previous crash.

If we had it in one loop, a crash would restart the worker but there's a chance it would then skip advancing deletion schedules leaving them in stale state forever.

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.

Nevermind, you were right, will fix that 🫡

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.

3 participants