Problem
Three independent code paths call AccountMergeService.sendMergeRequest(master, slave, reason) for what is logically the same merge event, and each call independently queues an ACCOUNT_MERGE_REQUEST mail through notificationService.sendMail (src/subdomains/generic/user/models/account-merge/account-merge.service.ts:63–88). The user receives the merge request mail more than once.
Triggers:
| Reason |
Trigger location |
MergeReason.IDENT_DOCUMENT |
src/subdomains/generic/kyc/services/kyc.service.ts:1253 and :1461 (ident step verification + the "Aktualisieren" / re-check path) |
MergeReason.IBAN |
src/subdomains/generic/user/models/bank-data/bank-data.service.ts:443 |
MailContext.CHANGED_MAIL (separate path) |
user-data-notification.service.ts:98–151 called from user-data.service.ts:1235 |
The debounce: 60000 set on the mail (account-merge.service.ts:86) only suppresses sends with the same correlationId AND same context within 60 s — see the suppression rule in notification.entity.ts:53–58. The correlationId is set to request.id (the freshly-created AccountMerge row's id), so two sendMergeRequest calls for the same master/slave pair produce two different AccountMerge rows with two different ids, and the debounce never matches.
User impact
Reported as part of the post-merge UX: the user gets two identical "merge request" mails for one merge event, plus an additional notification on a subsequent app refresh. Erodes trust in the flow.
Suggested fix shape
Dedup at the sendMergeRequest entry, not at the mail layer. Before creating a new AccountMerge row + sending the mail, query for an existing open merge for the same {master, slave, reason} (and !isCompleted && !isExpired). If one exists, return early — reuse the existing request rather than minting a new one.
Subsequent improvement (optional): allow a follow-up reason to be added to an open merge (e.g. an IBAN conflict surfaces after an existing ident-document merge is already open) without sending another mail — the existing mail already directed the user to the confirmation URL.
Pair-PR
No app-side change. Pure backend deduplication.
Source
Surfaced in DFXswiss/realunit-app#611 (item 2) from the same test session as the other merge bugs.
Problem
Three independent code paths call
AccountMergeService.sendMergeRequest(master, slave, reason)for what is logically the same merge event, and each call independently queues anACCOUNT_MERGE_REQUESTmail throughnotificationService.sendMail(src/subdomains/generic/user/models/account-merge/account-merge.service.ts:63–88). The user receives the merge request mail more than once.Triggers:
MergeReason.IDENT_DOCUMENTsrc/subdomains/generic/kyc/services/kyc.service.ts:1253and:1461(ident step verification + the "Aktualisieren" / re-check path)MergeReason.IBANsrc/subdomains/generic/user/models/bank-data/bank-data.service.ts:443MailContext.CHANGED_MAIL(separate path)user-data-notification.service.ts:98–151called fromuser-data.service.ts:1235The
debounce: 60000set on the mail (account-merge.service.ts:86) only suppresses sends with the samecorrelationIdAND samecontextwithin 60 s — see the suppression rule innotification.entity.ts:53–58. ThecorrelationIdis set torequest.id(the freshly-createdAccountMergerow's id), so twosendMergeRequestcalls for the same master/slave pair produce two differentAccountMergerows with two different ids, and the debounce never matches.User impact
Reported as part of the post-merge UX: the user gets two identical "merge request" mails for one merge event, plus an additional notification on a subsequent app refresh. Erodes trust in the flow.
Suggested fix shape
Dedup at the
sendMergeRequestentry, not at the mail layer. Before creating a newAccountMergerow + sending the mail, query for an existing open merge for the same{master, slave, reason}(and!isCompleted && !isExpired). If one exists, return early — reuse the existing request rather than minting a new one.Subsequent improvement (optional): allow a follow-up reason to be added to an open merge (e.g. an IBAN conflict surfaces after an existing ident-document merge is already open) without sending another mail — the existing mail already directed the user to the confirmation URL.
Pair-PR
No app-side change. Pure backend deduplication.
Source
Surfaced in
DFXswiss/realunit-app#611(item 2) from the same test session as the other merge bugs.