Google App Engine 超时:数据存储操作超时,或数据暂时不可用

Posted

技术标签:

【中文标题】Google App Engine 超时:数据存储操作超时,或数据暂时不可用【英文标题】:Google App Engine timeout: The datastore operation timed out, or the data was temporarily unavailable 【发布时间】:2011-06-12 08:17:32 【问题描述】:

这是我每天都会在我的应用程序日志中看到的常见异常,通常每天 5/6 次,访问量为 1K/天:

db error trying to store stats
Traceback (most recent call last):
  File "/base/data/home/apps/stackprinter/1b.347728306076327132/app/utility/worker.py", line 36, in deferred_store_print_statistics
    dbcounter.increment()
  File "/base/data/home/apps/stackprinter/1b.347728306076327132/app/db/counter.py", line 28, in increment
    db.run_in_transaction(txn)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 1981, in RunInTransaction
    DEFAULT_TRANSACTION_RETRIES, function, *args, **kwargs)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 2067, in RunInTransactionCustomRetries
    ok, result = _DoOneTry(new_connection, function, args, kwargs)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 2105, in _DoOneTry
    if new_connection.commit():
  File "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1585, in commit
    return rpc.get_result()
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 530, in get_result
    return self.__get_result_hook(self)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1613, in __commit_hook
    raise _ToDatastoreError(err)
Timeout: The datastore operation timed out, or the data was temporarily unavailable.

引发上述异常的函数如下:

def store_printed_question(question_id, service, title):
    def _store_TX():
        entity = Question.get_by_key_name(key_names = '%s_%s' % \
                                         (question_id, service ) )
        if entity:
            entity.counter = entity.counter + 1                
            entity.put()
        else:
            Question(key_name = '%s_%s' % (question_id, service ),\ 
                          question_id ,\
                          service,\ 
                          title,\ 
                          counter = 1).put()
    db.run_in_transaction(_store_TX)

基本上,store_printed_question 函数会检查给定问题是否先前已打印,在这种情况下会在单个事务中增加相关计数器。 此函数由 WebHandler 使用预定义的 default 队列添加到 deferred 工作人员,您可能知道,该队列的吞吐率为每秒五个任务调用。

在具有六个属性(两个索引)的实体上,我认为使用受延迟任务速率限制调节的 transactions 可以让我避免数据存储超时,但是查看日志,这个错误仍然每天都出现。

我存储的这个计数器不是很重要,所以我不担心会出现这些超时;无论如何,我很好奇为什么即使以每秒 5 个任务的低速率,Google App Engine 也无法正确处理此任务,如果降低速率可能是一种可能的解决方案。 在每个问题上使用sharded counter 以避免超时对我来说太过分了。

编辑: 我已将默认队列上的速率限制设置为每秒 1 个任务;我仍然遇到同样的错误。

【问题讨论】:

延迟任务在任何意义上都不会比常规任务“更轻”,只是它们更容易编写。在引擎盖下,它们是通过常规处理程序实现的。无论如何,这不会影响事务本身的开销。 【参考方案1】:

查询只能存在 30 秒。请参阅我对this question 的回答,了解一些使用游标分解查询的示例代码。

【讨论】:

api 调用的默认超时为 5 秒。您可以通过像这样配置上下文来更改它: ctx := appengine.Timeout(appengine.NewContext(req), 30*time.Second) appengine.Timeout 现在context.WithTimeout【参考方案2】:

一般来说,像这样的超时通常是因为write contention。如果您有一个事务正在进行,并且您正在同时向同一个实体组写入一堆东西,那么您会遇到写入争用问题(optimistic concurrency 的副作用)。在大多数情况下,如果您将entity group 变小,通常可以最大限度地减少此问题。

在您的具体情况下,根据上面的代码,很可能是因为您应该使用sharded counter 来避免序列化写入的堆叠。

另一个不太可能的可能性(此处仅出于完整性提及)是您的数据所在的平板电脑是being moved。

【讨论】:

没有定义实体组,我正在更新单个模型。实体组上的写入争用通常会引发另一种类型的异常。正如我在问题中所写,在这种情况下使用分片计数器似乎有点过头了。 @systempuntoout 如果您对单个实体进行超过 1QPS 的修改,您仍然可以对它们进行写入争用。争用导致超时异常。 @Nick 是的,实体组的写入争用导致“实体组与密钥的事务冲突..”错误。 我会补充一点,有时在相同的方法上我得到 ApplicationError: ApplicationError: 5 那是另一个超时错误。

以上是关于Google App Engine 超时:数据存储操作超时,或数据暂时不可用的主要内容,如果未能解决你的问题,请参考以下文章

连接到 Google App Engine 数据存储

python Google App Engine:将CSV导入数据存储区

在 Google App Engine 数据存储中存储分层数据?

从 Google App Engine 数据存储中获取不一致

如何减少 Google App Engine 数据存储延迟?

从 Google App Engine 数据存储区获取随机记录?