應用程序引擎數據存儲當然有downtime。但是,我希望有一個「故障安全」put,它在面對數據存儲錯誤時更加穩健(請參閱下面的動機)。當數據存儲不可用時,似乎任務隊列是延遲寫入的明顯位置。我不知道任何其他解決方案(除了通過urlfetch將數據發送給第三方)。應用程序引擎上的故障安全數據存儲更新
動機:我真的需要要被放置在數據存儲實體 - 只是顯示錯誤信息給用戶不會做。例如,也許有一些副作用發生,不容易撤銷(可能與第三方網站有一些互動)。
我想出了一個簡單的包裝(我認爲)提供了一個合理的「故障安全」放(見下文)。你有沒有看到這方面的問題,或有一個更強大的實施的想法? (注:由於張貼在由尼克·約翰遜和薩克森德魯斯答案的建議,這個帖子被編輯了一些改進的代碼)的任務
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
我不要期待這個用於每把 - 大部分時間,顯示錯誤信息就好了。對於每一個放置都使用它是很有誘惑力的,但是我認爲有時如果我告訴他們他們的改變將在以後出現(並且繼續向他們顯示舊數據直到數據存儲備份和延期賣出執行)。
一個相關的問題:是否有數據存儲和任務隊列停機之間有任何相關性?(http://stackoverflow.com/questions/3800252/datastore-and-task-queue-downtime-correlation) – 2010-09-26 23:25:45