Google app engine 如何确保使用任务队列时不会发送两次电子邮件?

Google app engine 如何确保使用任务队列时不会发送两次电子邮件?,google-app-engine,email,cron,task-queue,Google App Engine,Email,Cron,Task Queue,我想更改我的GAE应用程序逻辑,并开始使用任务队列发送电子邮件 目前我有一个cron作业,每15分钟运行一次,读取要从数据存储发送的消息: class SendMessagesHandler(webapp2.RequestHandler): def get(self): emails_quota_exceeded = models.get_system_value('emails_quota_exceeded') if emails_quota_excee

我想更改我的GAE应用程序逻辑,并开始使用任务队列发送电子邮件

目前我有一个cron作业,每15分钟运行一次,读取要从数据存储发送的消息:

class SendMessagesHandler(webapp2.RequestHandler):
    def get(self):
        emails_quota_exceeded = models.get_system_value('emails_quota_exceeded')
        if emails_quota_exceeded == 0 or emails_quota_exceeded == None:
            messages = models.get_emails_queue()
            for message in messages:
                try:
                    ...
                    email.send()
                    models.update_email_status(message.key.id()) # update email status indicating that the mail has been sent 
                except apiproxy_errors.OverQuotaError, error_message:
                    models.set_system_value(what='emails_quota_exceeded', val=1)
                    logging.warning('E-mails quota exceeded for today: %s' % error_message)
                    break
        else:
            logging.info('Free quota to send e-mails is exceeded')
如果我使用任务队列,那么我将得到如下结果:

for message in messages:
    taskqueue.add(url='/sendmsg', payload=message)
在这个场景中,同一条消息可能会被发送两次(甚至更多次)——例如,如果它还没有发送,但cron作业是第二次执行的。 如果在将邮件添加到队列后立即更新电子邮件状态:

for message in messages:
    taskqueue.add(url='/sendmsg', payload=message)
    models.update_email_status(message.key.id()) # update email status indicating that the mail has been sent 
那么,可能永远不会发送该消息。例如,如果在电子邮件发送过程中发生异常。请理解任务将重试,但如果今天超出配额,则重试将无济于事

我想我还可以在尝试发送任务队列之前重新读取每个消息的状态,但这将花费我额外的读取操作


处理该任务的最佳方法是什么?

为任务指定一个名称,包括
key.id()
将防止任务被发送两次:

task_name = ''.join(['myemail-', str(mykey)])
try:
    taskqueue.Task(
        url="/someURL/send-single-email",
        name=task_name,
        method="POST",
        params={
           "subject": subject,  
           "body": body,
           "to": to,
           "from": from }
    ).add(queue_name="mail-queue")
except:
    pass #throws TombstonedTaskError(InvalidTaskError) if tombstoned name used.
有时,您可能希望为具有相同密钥的消息发送后续电子邮件。因此,我建议在任务名称中添加
date
datetime
标记。这将允许您稍后发送相同密钥的其他消息:

 task_name = ''.join(['myemail-', str(mykey), str(datetime.utcnow()-timedelta(hours=8))]).translate(string.maketrans('.:_ ', '----'))

阅读不是很贵。您可以向自己发送密件抄送,以确保已发送。@voscausa,是否每天复制100封邮件?不,谢谢:)有趣的方法,谢谢。但是,您从哪里找到了任务名称必须唯一的信息,以及任务名称不唯一时引发的
tombstonedtaskeror
?我没有立即看到文档,但任务名称正是为了这个目的而实现的,因此任务不会重复执行。另外,我应该补充一点,如果您想多次发送相同密钥的邮件,您还可以在任务名称中添加
date
datetime
。这将允许您稍后发送相同密钥的邮件<代码>''.join(['myemail-',str(mykey),str(datetime.utcnow()-timedelta(hours=8))).translate(string.maketrans('.:,'---'))Aah,我在那里找到了描述-。我已经编辑了答案,在任务名称中包含了
datetime
戳的建议。嗯,有时,我得到的任务是现成的,而不是墓碑上的。。。看起来我应该抓住这两个例外。