Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/neo4j/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
App engine ndb 我应该如何避免使用mailgun、taskqueue和ndb发送重复的电子邮件?_App Engine Ndb_Mailgun_Task Queue - Fatal编程技术网

App engine ndb 我应该如何避免使用mailgun、taskqueue和ndb发送重复的电子邮件?

App engine ndb 我应该如何避免使用mailgun、taskqueue和ndb发送重复的电子邮件?,app-engine-ndb,mailgun,task-queue,App Engine Ndb,Mailgun,Task Queue,我正在使用taskqueue API通过mailgun发送多封电子邮件。我的代码大致如下所示: 等级CpMsg(ndb.型号): group=ndb.KeyProperty() sent=ndb.BooleanProperty() #其他属性 def发送邮件(消息): “”“向mailgun的API发送请求”“” #一些代码 通过 类MailTask(TaskHandler): def post(自我): p_key=utils.key_from_字符串(self.request.get('p'

我正在使用taskqueue API通过mailgun发送多封电子邮件。我的代码大致如下所示:

等级CpMsg(ndb.型号):
group=ndb.KeyProperty()
sent=ndb.BooleanProperty()
#其他属性
def发送邮件(消息):
“”“向mailgun的API发送请求”“”
#一些代码
通过
类MailTask(TaskHandler):
def post(自我):
p_key=utils.key_from_字符串(self.request.get('p'))
msgs=CpMsg.query(
CpMsg.group==p_键,
CpMsg.sent==False).fetch(批大小)
如果MSG:
发送邮件(msgs)
对于msg中的msg:
msg.sent=True
ndb.put\U multi(msgs)
#在冷却秒后再次调用该任务
上面的代码工作得很好,但根据文档,taskqueue API保证任务至少交付一次,因此任务应该是幂等的。现在,大多数情况下上述代码都是这样,因为它只获取“sent”属性等于False的消息。问题在于非祖先ndb查询最终是一致的,这意味着如果任务连续快速执行两次,查询可能会返回过时的结果,并包含刚刚发送的消息

我曾想过为消息添加一个祖先,但由于发送的电子邮件数量将达到数千封,我担心这可能意味着拥有大型实体组,而实体组的写入吞吐量有限


我应该使用祖先进行查询吗?或者有一种方法可以配置mailgun以避免发送同一封电子邮件两次?我是否应该接受这样的风险:在某些罕见的情况下,可能会多次发送几封电子邮件?

避免最终一致性障碍的一种可能的方法是将查询设置为仅
keys\u
One,然后通过关键字查找(强一致性)迭代消息关键字以获得实际消息,检查
msg.sent
是否为真,并在这种情况下跳过发送这些消息。大致如下:

    msg_keys = CpMsg.query(
        CpMsg.group==p_key,
        CpMsg.sent==False).fetch(BATCH_SIZE, keys_only=True)
    if not msg_keys:
        return

    msgs = ndb.get_multi(msg_keys)
    msgs_to_send = []

    for msg in msgs:
         if not msg.sent:
             msgs_to_send.append(msg)
    if msgs_to_send:
        send_mail(msgs_to_send)

        for msg in msgs_to_send:
            msg.sent = True

        ndb.put_multi(msgs_to_send)
您还必须使您的
post
调用具有事务性(使用
@ndb.transactional()
decorator)


这应该解决由查询一致性引起的重复问题。但是,由于数据存储争用(或任何其他原因)而导致的事务重试仍然存在重复的空间,因为
send\u mail()
调用不是幂等的。一次发送一条消息(可能使用任务队列)可以减少发生这种情况的机会。另请参见

谢谢Dan。我认为我不能在事务中执行非祖先查询,因此除非我先执行查询,然后将结果作为参数传递,否则这是行不通的。不过,还有另一个问题,因为我最多只能修改事务中的25个实体组(GAE数据存储限制),这将对批处理大小设置硬限制。啊,对,忘了-是的,首先进行查询,然后将密钥列表传递给事务函数,以查找和检查实体。鉴于我只能更新事务中的25个实体,这将导致另一个问题,因为我需要更多的任务和API调用。也许这种情况发生的风险和后果不值得这样的改变。您认为我应该进行这些更改以减少这种已经很小的可能性,还是等到迁移到新Firestore完成后再进行更改,这样可以消除问题?是的,围绕每个事务的25个实体组工作可能会有点麻烦。只有您可以评估应用程序案例的价值。我也在考虑迁移到Firestore,但由于我有点怀疑,我肯定会在承诺之前做一些性能测试,因为我怀疑这些功能会付出性能代价。