【问题标题】:How can I avoid sending duplicate emails when I commit the "email sent" action after sending the email?发送电子邮件后提交“已发送电子邮件”操作时,如何避免发送重复的电子邮件?
【发布时间】:2012-07-31 21:10:28
【问题描述】:

当用户在我的网站上注册时,我会在数据库中创建一条用户记录,其中 email_sent = 'NO'。然后我有一个 cron 作业,每隔几分钟发送一次欢迎电子邮件。它选择 email_sent = 'NO' 的所有用户记录并发送欢迎电子邮件。

如果电子邮件发送成功,则将用户记录更新为 email_sent='YES'。我的问题是,如何避免电子邮件发送成功但用户记录更新失败并且下次运行 cron 作业时会发送重复电子邮件的情况。即使我使用了数据库事务并且我在发送电子邮件之前更新了记录但之后又提交了,如果提交失败,我也会遇到同样的情况,发送重复的电子邮件。

另一种方法是将记录更新为 email_sent='YES' 并立即提交,然后尝试发送电子邮件。但如果电子邮件发送失败,则不会发送电子邮件。

有什么方法可以保证邮件只发送一次吗?我意识到由于许多外部因素,电子邮件传递最终可能会失败,但我只想确保我的 SMTP 服务器成功地将其关闭一次。

【问题讨论】:

  • 为什么更新用户记录失败?解决这个问题,你的问题就解决了。
  • 您使用什么 RDBMS,Oracle、SQL-Server、MySQL 等?很可能会有一些特定于数据库的东西可以提供帮助。
  • 进一步罗伯特哈维的评论 - 如果您的提交在更新发送的记录时不成功,那么几乎没有什么可以防止重复。如果 cron 依赖于数据库中的值,则必须可靠地更新该值。否则,您将不得不开发更复杂的方法来处理不一致的数据。
  • 目前没有失败,但我很偏执,担心它会失败。
  • @user1566921 你能设置一个日志来查看重复电子邮件的发送频率吗?然后你可以看看实际重复率是否高于{AcceptableRate}。

标签: database email distributed-transactions


【解决方案1】:

您的评论指出提交没有失败,因此您可能担心风。

使事务尽可能小,并让它失败,无论哪种方式在您的系统中更可接受...

BeginTransaction;
LookupRecord;

SendMail;

if(SentSuccessfully)
{
    UpdateRecord(Sent=True);
    CommitTransaction;
}
else
{
    RollbackTransaction;
}

或者,您可以使用更复杂的方式完成此操作,并且无需围绕电子邮件功能进行交易,但我认为这不是更好:

BeginTransaction;
LookupRecord;
UpdateRecord(Sent=True);
CommitTransaction;

SendMail;

if(!SentSuccessfully)
{
    UpdateRecord(Sent=False);
}

无论哪种方式,如果您有错误并且没有将正确的值提交给数据库,那么在不引入(可能不必要的)复杂性的情况下,您无法避免它。您确实应该专注于使更新和事务坚如磐石,以免更新失败。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多