taskqueue APIを使用して複数のメールを送信していますが、これはmailgunを使用した小さなグループです。私のコードは多かれ少なかれ次のようになります:
class CpMsg(ndb.Model):
group = ndb.KeyProperty()
sent = ndb.BooleanProperty()
#Other properties
def send_mail(messages):
"""Sends a request to mailgun's API"""
# Some code
pass
class MailTask(TaskHandler):
def post(self):
p_key = utils.key_from_string(self.request.get('p'))
msgs = CpMsg.query(
CpMsg.group==p_key,
CpMsg.sent==False).fetch(BATCH_SIZE)
if msgs:
send_mail(msgs)
for msg in msgs:
msg.sent = True
ndb.put_multi(msgs)
#Call the task again in COOLDOWN seconds
上記のコードは正常に機能していますが、ドキュメントによると、taskqueue APIは、タスクが少なくとも1回配信されることを保証しているため、タスクはべき等である必要があります。さて、ほとんどの場合、これは上記のコードの場合に当てはまります。これは、「sent」プロパティがFalseに等しいメッセージのみを取得するためです。問題は、祖先以外のndbクエリは結果整合性しか持たないことです。つまり、タスクが2回連続して実行されると、クエリは古い結果を返し、送信されたばかりのメッセージを含める可能性があります。
メッセージの祖先を含めることを考えましたが、送信される電子メールは数千通になるので、書き込みスループットが制限されている大規模なエンティティグループがあることを意味するのではないかと心配しています。
クエリを作成するために祖先を使用する必要がありますか?または、同じ電子メールを2回送信しないようにmailgunを構成する方法はありますか?まれに、いくつかの電子メールが複数回送信される可能性があるというリスクを受け入れる必要がありますか?
結果整合性のハードルを回避するための1つの可能なアプローチは、クエリをkeys_only
1つにしてから、メッセージキーを反復処理して、キールックアップ(強い整合性)によって実際のメッセージを取得し、msg.sent
Trueかどうかを確認し、そのような場合はそれらのメッセージの送信をスキップすることです。これらの線に沿った何か:
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()
デコレータを使用して)通話をトランザクションで行う必要があります。
これは、クエリの結果整合性によって引き起こされる重複に対処する必要があります。ただし、send_mail()
呼び出しはべき等ではないため、データストアの競合(またはその他の理由)によるトランザクションの再試行によって重複が発生する可能性があります。一度に1つのメッセージを送信すると(おそらくタスクキューを使用して)、その可能性を減らすことができます。GAE / P:API呼び出しによるトランザクションの安全性も参照してください。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加