One phone, one alert — collapse duplicate push destinations

FixMediaService
Shipped
August 26, 2026 at 5:58 AM UTC
Author
Kamo
Commit
8a809b3

Members were getting the same chat message three-plus times on their phone. Registrations are unique on (device_token_id, org_id, push_kind), NOT on the push token. Signing in again on the same handset mints a brand-new user_device_tokens row (device/register mints unconditionally), while FCM keeps handing back the SAME token — so every sign-in left one more live registration row pointing at the one phone, and doDispatch sent the identical message once per row. Production carried five such rows for a single handset: org 1168485648209608710 / member 1168485658174423046 5 registration rows | 5 device_token_ids | 1 distinct push_token 21 rows across the whole table resolved to just 2 real devices. Neither existing guard could catch it. The per-conversation rate limit is evaluated ONCE, before the fan-out, so it rate-limits events, not sends. The FCM collapse key only collapses messages still QUEUED for an offline device, never ones already delivered. And FcmSender's stale-registration pruning never fires, because the duplicated token is perfectly valid — FCM returns UNREGISTERED for dead tokens, not for extra rows naming a live one. The push token IS the destination, so it is now the identity: distinct- Destinations keeps the most recently updated row per (pushKind, pushToken) and drops the rest, leaving genuinely distinct devices untouched. Rows with a blank token are dropped rather than allowed to stand in for a destination. This fixes existing rows without a migration. The companion change in SecurityService stops them accumulating in the first place.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing