fix: send trial emails synchronously so sent flag reflects delivery#201
Open
dan2k3k4 wants to merge 2 commits into
Open
fix: send trial emails synchronously so sent flag reflects delivery#201dan2k3k4 wants to merge 2 commits into
dan2k3k4 wants to merge 2 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sends trial emails within the job so the sent flag is only set after a successful send, allowing retries when delivery fails.
Greptile Summary
This PR fixes a bug where trial emails could be marked as sent even when delivery failed, by switching
Mail::queue()toMail::send()so delivery is synchronous within the job and the sent flag is only updated after all owners have been emailed successfully. Three new feature tests are added to verify the happy path and the flag-stays-false-on-failure contract for each job.queue()→send()in all three trial email jobs — makes email delivery a blocking operation so any transport exception propagates before the*_email_sentflag is persisted, enabling the queue's built-in retry to re-attempt delivery.ProcessMidtrialEmailJobTest,ProcessOneDayLeftEmailJobTest,ProcessTrialCompleteEmailJobTest) — each covers the success path (email dispatched, flag set) and the send-failure path (exception thrown, flag stays false).Confidence Score: 4/5
Safe to merge for single-owner groups; groups with multiple owners risk duplicate emails to already-contacted owners when a mid-loop send failure triggers a retry.
The synchronous-send fix is correct and the new tests cover the retry contract well. The unresolved concern (already noted in the previous round) is that the owner loop has no per-owner progress tracking: if the second owner's send throws, the job retries from the first owner on the next attempt, so the first owner receives the email twice. Until that is addressed, deployments where userGroup can have more than one owner carry a real duplicate-email risk.
The foreach owner loops in all three job files (ProcessTrialCompleteEmailJob.php lines 44–57, ProcessMidtrialEmailJob.php lines 62–81, ProcessOneDayLeftEmailJob.php lines 50–68) need attention if multi-owner groups are possible in production.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Q as Queue Worker participant J as Trial Email Job participant M as Mail::send() participant DB as Database Q->>J: execute handle() J->>J: guard checks (is_trial, flag, schedule) alt guards pass loop foreach owner J->>M: send(Mailable) [synchronous] alt send succeeds M-->>J: ok else send fails M-->>J: throws Exception J-->>Q: re-throw (flag stays false) Q->>J: retry job (starts over from owner 1) end end J->>DB: "update sent flag = true" else guards fail J-->>Q: return (no-op) end%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Q as Queue Worker participant J as Trial Email Job participant M as Mail::send() participant DB as Database Q->>J: execute handle() J->>J: guard checks (is_trial, flag, schedule) alt guards pass loop foreach owner J->>M: send(Mailable) [synchronous] alt send succeeds M-->>J: ok else send fails M-->>J: throws Exception J-->>Q: re-throw (flag stays false) Q->>J: retry job (starts over from owner 1) end end J->>DB: "update sent flag = true" else guards fail J-->>Q: return (no-op) endComments Outside Diff (1)
app/Jobs/ProcessPolydockAppInstanceJobs/Trial/ProcessTrialCompleteEmailJob.php, line 44-60 (link)When
userGroup->ownerscontains more than one owner and asend()call fails mid-loop (e.g., on the second owner), the exception propagates beforetrial_complete_email_sentis set totrue. On the next retry the job starts over from the first owner, so every owner who was already emailed successfully in the previous attempt receives a duplicate. The same pattern exists inProcessMidtrialEmailJob(line 62–81) andProcessOneDayLeftEmailJob(line 50–68).A defensive approach is to track sent owners within the job run and skip any that were already emailed — for example, by collecting successfully delivered addresses in a local array before the loop and checking it at the top of each iteration, or by restructuring to catch and re-throw after recording progress. As long as the
userGrouptypically has exactly one owner this is low-risk, but the code makes no such assumption.Reviews (2): Last reviewed commit: "test: cover midtrial and one-day-left em..." | Re-trigger Greptile