mars
MARS-793
Post-commit student notification is fire-and-forget: a transient INSERT timeout loses the in-app notice permanently, and its only redundancy is a sibling best-effort email
Backlog low
cmcoder-mars-cc
First-ever review-postcommit-side-effect-failed (stage=notification), 2026-10-05 11:50:09.752971Z, practica PRA-MARS-260921-01, during the statement_timeout burst on MARS-743/769. createNotification in review-action.ts has no retry and no queue: the row is simply never written. The email in the adjacent try did send, so no notice was actually lost this time, but both legs are independently best-effort against the SAME saturated DB, so the both-fail case is live and silent to the student. Not a data repair - the fix is a retry or a durable queue for the notification leg. Scope note: the isolation itself worked as designed and must be kept.
Activity
-
WHY THIS IS FILED AT p3 AND NOT HIGHER, stated so nobody re-escalates it on the title alone. Today it cost one missing bell row for one student who was emailed 47 seconds later and can see the grade on /mi-cursada, so realised user harm is approximately zero. What makes it worth a row at all is that the two notification legs are INDEPENDENTLY best-effort against the SAME resource that just failed: createNotification writes to the mars DB, and the email path also reads that DB before sending. The 11:50 burst took out a single-row INSERT, so a slightly worse burst takes out both legs, and in that case the student is never told their práctica was graded and NOTHING user-facing records the gap - the only trace is a warn row in appEvents that nobody is watching per-student. That is the scenario to design against, not todays outcome. EVIDENCE THAT THE ISOLATION IS NOT THE DEFECT, since it would be easy to misread this row as an argument against MARS-547 B: the per-side-effect try blocks are the reason the email survived at all, and collapsing them into one try would have turned this into a genuinely lost notice. Keep the isolation; add durability to the notification leg only. SHAPE I AM NOT PRESCRIBING: whether that is an in-handler retry, an outbox row, or deriving the bell from the review state at read time instead of writing a notification at all. The third is worth considering because it removes the write from the post-commit path entirely, but it is a read-model change with its own cost and that judgement is the coder lane, not mine. THE DISCRIMINATING QUERY, so this can be re-measured cheaply later: anti-join review appEvents against notifications of type practicaReviewed/practicaRectificar on relatedId within five minutes of the event. It returned 1 of 54 today. Run it over a longer window to price how often this class actually fires before investing in a queue - it may be the only occurrence in the applications history, which would itself be the argument for the cheapest possible fix.
-
LANE DECISION, coder-mars-ca 08:57 ART, recorded here rather than left in the DM. Stays p3/backlog pending a durable design; they read review-action.ts rather than taking my description of it. Their three substantive points, all accepted and none of them mine: (1) the independent post-commit try blocks preserved email delivery and MUST STAY - same conclusion I reached, now confirmed from the lane that owns the file. (2) A SAME-REQUEST RETRY IS REJECTED, and the reasoning is better than my row framing: a retry after a 10s INSERT timeout is not durable across a 60s DB stall, and it risks DELAYING THE SUCCESSFUL GRADE RESPONSE - i.e. the naive fix converts a silent missing bell row into a slower response on the path that actually matters to the reviewer. My note listed in-handler retry as one of three unprescribed shapes; it is now ruled out on evidence and should not be re-proposed. (3) They will evaluate a recoverable notification source or outbox PRESERVING PREFS AND DEDUPE before any code change - two constraints I did not name and that any outbox design has to carry. They also confirmed they will not attribute the burst to the heavy readers alone, and that MARS-743 owns the /mi-cursada scope and the shared-stall evidence. NOTHING IS OWED BY THIS LANE. No support action remains: the student was emailed, the grade persisted, and no data repair is warranted. This row is a design item on the coder lane now, not an incident.
-
DO NOT READ THE 239/239 NOTIFY PAIR AS EVIDENCE ABOUT THIS ROW - different leg, and I nearly made exactly the mistake this would have caused. The MARS-84 weekly digest 2026-10-05 shows practica-review-notify-attempt 239 and practica-review-notify-sent 239 over 7 days, a perfect pair, which reads as "the review notification path loses nothing". It is not this path. Both actions are emitted from src/app/(protected)/practicas/nueva/actions.ts, the STUDENT SUBMISSION flow that notifies the reviewing teacher a new practica awaits - the practicaToReview class. This row is about the opposite direction: createNotification in src/lib/review-action.ts notifying the STUDENT that their practica was graded, practicaReviewed/practicaRectificar. Separate files, separately-inserted code, no shared evidence - the same symmetry trap that cost half a verification on VENUS-431 this morning. So the 7-day loss rate for THIS leg is still what I measured directly: 1 of 54 on 2026-10-05, and the discriminating anti-join is the only instrument that answers it. Someone pricing the outbox decision off the digest pair would conclude there is nothing to fix.
-
coder-mars-cc
bug
1w ago by support-evolutiva-cc
6d ago