gpt4 book ai didi

google-app-engine - 应用引擎上的故障安全数据存储更新

转载 作者:行者123 更新时间:2023-12-04 06:45:25 24 4
gpt4 key购买 nike

应用引擎数据存储区当然有 downtime .但是,我想要一个“故障安全”put这在面对数据存储错误时更加健壮(请参阅下面的动机)。当数据存储不可用时,任务队列似乎是一个明显的推迟写入的地方。我不知道任何其他解决方案(除了通过 urlfetch 将数据发送给第三方)。

动机 :我有一个真正需要放入数据存储区的实体 - 仅向用户显示错误消息是行不通的。例如,可能发生了一些无法轻易撤消的副作用(可能是与第三方站点的一些交互)。

我想出了一个简单的包装器,它(我认为)提供了一个合理的“故障安全”放置(见下文)。您是否发现这有任何问题,或者对更强大的实现有什么想法? (注意:感谢 Nick Johnson 和 Saxon Druce 在答案中提出的建议,这篇文章已经过编辑,并对代码进行了一些改进。)

import logging
from google.appengine.api.labs.taskqueue import taskqueue
from google.appengine.datastore import entity_pb
from google.appengine.ext import db
from google.appengine.runtime.apiproxy_errors import CapabilityDisabledError

def put_failsafe(e, db_put_deadline=20, retry_countdown=60, queue_name='default'):
"""Tries to e.put(). On success, 1 is returned. If this raises a db.Error
or CapabilityDisabledError, then a task will be enqueued to try to put the
entity (the task will execute after retry_countdown seconds) and 2 will be
returned. If the task cannot be enqueued, then 0 will be returned. Thus a
falsey value is only returned on complete failure.

Note that since the taskqueue payloads are limited to 10kB, if the protobuf
representing e is larger than 10kB then the put will be unable to be
deferred to the taskqueue.

If a put is deferred to the taskqueue, then it won't necessarily be
completed as soon as the datastore is back up. Thus it is possible that
e.put() will occur *after* other, later puts when 1 is returned.

Ensure e's model is imported in the code which defines the task which tries
to re-put e (so that e can be deserialized).
"""
try:
e.put(rpc=db.create_rpc(deadline=db_put_deadline))
return 1
except (db.Error, CapabilityDisabledError), ex1:
try:
taskqueue.add(queue_name=queue_name,
countdown=retry_countdown,
url='/task/retry_put',
payload=db.model_to_protobuf(e).Encode())
logging.info('failed to put to db now, but deferred put to the taskqueue e=%s ex=%s' % (e, ex1))
return 2
except (taskqueue.Error, CapabilityDisabledError), ex2:
return 0

任务的请求处理程序:
from google.appengine.ext import db, webapp

# IMPORTANT: This task deserializes entity protobufs. To ensure that this is
# successful, you must import any db.Model that may need to be
# deserialized here (otherwise this task may raise a KindError).

class RetryPut(webapp.RequestHandler):
def post(self):
e = db.model_from_protobuf(entity_pb.EntityProto(self.request.body))
e.put() # failure will raise an exception => the task to be retried

我不希望每次放置都使用它 - 大多数情况下,显示错误消息就好了。每次 put 都使用它很诱人,但我认为有时如果我告诉用户他们的更改将在稍后出现(并继续向他们显示旧数据,直到数据存储备份和延迟),这可能会让用户更加困惑puts 执行)。

最佳答案

您的方法是合理的,但有几个警告:

  • 默认情况下,放置操作将重试,直到超时。由于您有备份策略,您可能希望尽快放弃 - 在这种情况下,您应该为 put 方法调用提供 rpc 参数,指定自定义截止日期。
  • 无需设置显式倒计时 - 任务队列将以增加的时间间隔为您重试失败的操作。
  • 您不需要使用 pickle - Protocol Buffers 具有自然的字符串编码,效率更高。见 this post以演示如何使用它。
  • 正如 Saxon 指出的那样,任务队列的有效负载限制为 10 KB,因此您可能会遇到大型实体的问题。
  • 最重要的是,这将数据存储一致性模型从“强一致性”更改为“最终一致性”。也就是说,您排入任务队列的 put 可以在将来的任何时间应用,覆盖在此期间所做的任何更改。任何数量的竞争条件都是可能的,如果任务队列上有待处理的放置,则本质上会使事务变得无用。
  • 关于google-app-engine - 应用引擎上的故障安全数据存储更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3800263/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com