A person taken out of a drip stays out

FixEmailService
Shipped
September 17, 2026 at 2:10 AM UTC
Author
Kamo
Commit
3497d41

The engine moves on up to 200 due people per drip from copies it read at the start of a pass, up to 25 seconds old by the time each is used. Meanwhile a member can take someone out, archive the drip, or add someone again who starts over. The engine saved its whole copy regardless, so a run just ended was written back to ACTIVE, and the person went on getting the drip; an email queued as the run ended was not cancelled either. Runs are now written through DripEnrollmentWrites, and every write only lands while the table still has the run going (WHERE status = 'ACTIVE'): - the engine's saves (rescheduling, completing, what it stores after queuing an email); - ending a run (removal, a status change or reassignment exit, a goal click, starting over), with UPDATE ... RETURNING so the email cancelled is the one the run is waiting on now, even if a pass queued it after the caller read the run; - archiving, which now ends everyone's run before cancelling unsent email. Just before queuing, the engine reads the run and its drip again: an ended run or a paused or archived drip gets no email. If its save loses after queuing, it cancels that email; queued rows get no release time until the dispatcher's next 30-second pass, so the cancel comes first. A drip paused while an email was being queued holds that email with the rest. Restarting a run goes through the advancer's exit, the same path as every other ending. Plain JDBC on purpose: requests run with an open session, where a JPA save writes the whole stale entity back. The statements were checked with EXPLAIN against the live schema.

All changes

Like what you see shipping?

All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.

Start Free ForeverView Pricing