Did you find a solution?
I once was struggling with a similar behavior. Users got hammered with emails, which the mail server sent but in discourse’s view were not sent.
My hypothesis was that it is possible that discourse gives a job to the mail server and there is a timeout during the process, which causes this to look like a fail to discourse even though the mail was sent. The failed job is then repeated and results in a new mail.
I was happy to resolve this by killing jobs in sidekiq, restarting the mail server and tinkering with fail2ban rules.