Stu Mason
Stu Mason

Activity

StuMason/cleanconnect
TidyLinker.com
TypeScript
Pull Request Merged

PR #167 merged: fix: stop notification + mailable queue jobs from burning out

Summary

Three production Sentry issue clusters in one bundle:

  • TIDY-LINKER-3J / 3H / 3K / 3MDirectBooking*Notification classes did not use SerializesModels, so the full JobPosting + User graphs were serialized into queue payloads. A deploy on 2026-05-26 13:20Z left in-flight payloads that unserialized to __PHP_Incomplete_Class, then every channel (Mail/Broadcast/Database) blew up on the type check, and the failed() handler tripped a final method_exists Error. Now using SerializesModels so payloads carry id refs only.
  • TIDY-LINKER-3AVerificationReminderMail (and all the other ThrottlesResendApi mailables) hit MaxAttemptsExceededException after 3 attempts because RateLimitedWithRedis releases the job back to the queue, and each release counts toward tries. Bulk nudges from admin produced bursts that exhausted attempts before the limiter could let them through. The trait now declares retryUntil(now()->addMinutes(30)) so throttled releases don't burn attempts.
  • TIDY-LINKER-2Y (unblocking) — JS errors in Sentry currently come back as at undefined:31 in None because sourcemaps aren't uploaded. Added the @sentry/vite-plugin to vite.config.ts, conditionally enabled when SENTRY_AUTH_TOKEN (or SENTRY_API_TOKEN) is set at build time. The plugin uploads maps to stu-ht/tidy-linker on the DE region and deletes them post-upload so they don't ship to the CDN. Next occurrence of 2Y should have a real stack.

Test plan

  • php artisan test --testsuite=Feature — 1196 passed, 0 failed (browser-suite failures are pre-existing, unrelated)
  • New tests/Feature/Notifications/DirectBookingSerializationTest.php — asserts each DirectBooking notification serializes/unserializes with ModelIdentifier refs (not full model bodies)
  • Extended tests/Feature/Mail/ThrottlesResendApiTest.php — asserts every throttled mailable's retryUntil() returns a future DateTimeInterface
  • npm run build succeeds locally (Sentry plugin no-ops without auth token)
  • vendor/bin/pint --dirty clean
  • After merge: clear failed_jobs queue on prod (php artisan queue:flush)
  • After merge: set SENTRY_AUTH_TOKEN env var on Coolify so the next prod build uploads sourcemaps
  • After merge: confirm Coolify post-deploy hook runs php artisan horizon:terminate so workers always reload current code (the original trigger for the deserialization cluster)
+543
additions
-4
deletions
10
files changed