Обновления Fail-Safe DataStore на App Engine
-
25-09-2019 - |
Вопрос
Дата хранения приложений, конечно, имеет время простоя. Отказ Тем не менее, я хотел бы иметь «неудачную безопасность» помещать Что более надежно перед лицом ошибок данных данных (см. Мотивацию ниже). Похоже, что очередь задач - это очевидное место, чтобы отложить пишеты, когда хранилище данных недоступен. Я не знаю о каких-либо других решениях, хотя (кроме доставки от данных на сторонние через URLFetch).
Мотивация: У меня есть сущность, которая В самом деле Необходимо разместить в DataStore - просто показывая сообщение об ошибке для пользователя не будет делать. Например, пожалуй, какой-то побочный эффект произошел, который не может быть легко отменить (возможно, некоторое взаимодействие с сторонним сайтом).
Я придумал простую обертку, которая (я думаю), обеспечивает разумную «неудачную» (см. Ниже). Вы видите какие-либо проблемы с этим или иметь представление о более надежной реализации? (Примечание. Благодаря предложениям, размещенным в ответах Ник Джонсон и Саксон-Дрю, этот пост был отредактирован с некоторыми улучшениями кода.)
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
я нет Ожидайте использовать это для каждый Поместите - большую часть времени, показывающее сообщение об ошибке просто в порядке. Заманчиво использовать его для каждого поставленного, но я думаю, что иногда это может быть более запутанным для пользователя, если я скажу им, что их изменения будут появляться позже (и продолжать показывать им старые данные, пока хранилище данных не будет а также Отложенные ставит выполнять).
Решение
Ваш подход разумно, но имеет несколько предостережений:
- По умолчанию операция поставленной операции повторится до тех пор, пока она не закончится вне времени. Поскольку у вас есть стратегия резервного копирования, вы можете сдаться раньше, и в этом случае вы должны предоставить параметр RPC на вызов метода PUT, указав на заказ срок.
- Нет необходимости устанавливать явный обратный отсчет - очередь задач повторится неудачные операции для увеличения интервалов.
- Вам не нужно использовать буферы Protocol Protocol, имеют натуральную строку, кодирующую, что намного эффективнее. Видеть эта почта Для демонстрации того, как его использовать.
- Как указывает саксон, полезные нагрузки очереди задач ограничены 10 килобайтами, поэтому у вас могут возникнуть проблемы с большими объектами.
- Самое главное, что это изменяет модель последовательности данных данных из «сильно последовательного» до «в конечном итоге последовательной». То есть, в любой момент в будущем можно было применить очередь в очереди задач, перезаписав любые изменения, которые были сделаны в промежуточном состоянии. Возможны любое количество условий расы, по сути, рендеринг транзакций бесполезных, если есть указывает на очередь задач.
Другие советы
Одна потенциальная проблема в том, что Задачи ограничены 10 КБ данных, Так что это не будет работать, если у вас есть сущность, которая больше, чем однажды замариваемое.