- 관련 상품
- 2026년 8월 26일 오전 5:58 UTC
- 이름 *
- Kamo
- 뚱 베어
- 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.