python - Google App Engine 中的争用问题

标签 python google-app-engine google-cloud-datastore app-engine-ndb contention

我在 Google App Engine 中遇到争用问题,并尝试了解发生了什么。

我有一个带有注释的请求处理程序:

@ndb.transactional(xg=True, retries=5) 

..在那个代码中,我获取了一些东西,更新了一些其他东西等等。但有时在请求期间日志中会出现这样的错误:

16:06:20.930 suspended generator _get_tasklet(context.py:329) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname"
path <
  Element {
    type: "PlayerGameStates"
    name: "hannes2"
  }
>
)
16:06:20.930 suspended generator get(context.py:744) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname"
  path <
    Element {
      type: "PlayerGameStates"
      name: "hannes2"
    }
  >
  )
16:06:20.930 suspended generator get(context.py:744) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname"
  path <
    Element {
      type: "PlayerGameStates"
      name: "hannes2"
    }
  >
  )
16:06:20.936 suspended generator transaction(context.py:1004) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname"
  path <
    Element {
      type: "PlayerGameStates"
      name: "hannes2"
    }
  >
  )

..后跟堆栈跟踪。如果需要,我可以更新整个堆栈跟踪,但有点长。

我不明白为什么会这样。查看我的代码中出现异常的那一行,我在一个完全不同的实体 (Round) 上运行 get_by_id。错误消息中提到的名为“hannes2”的“PlayerGameStates”是另一个实体 GameState 的父级,该实体已在几行之前从数据库中get_async:ed;

# GameState is read by get_async
gamestate_future = GameState.get_by_id_async(id, ndb.Key('PlayerGameStates', player_key))
...
gamestate = gamestate_future.get_result()
...

奇怪(?)的是,该实体没有写入数据存储。我的理解是,如果同一实体在同一时间并行更新,则可能会出现争用错误。或者如果在短时间内发生太多写入,则可能会出现争用错误。

但是在读取实体时也会发生这种情况吗? (“suspended generator get..”??)而且,这是在 5 次 ndb.transaction 重试之后发生的吗?我在日志中看不到任何表明已进行任何重试的内容。

非常感谢任何帮助。

最佳答案

是的,读取和写入操作都可能发生争用。

事务开始后 - 在您的情况下,当调用带有 @ndb.transactional() 注释的处理程序时 - 立即访问任何实体组(通过读或写操作,无关紧要)标记为这样。在那一刻,不知道在事务结束时是否会有写操作——这甚至都不重要。

过多的争用错误(与冲突错误不同!)表示有太多并行事务同时尝试访问同一实体组。即使没有事务实际尝试写入,它也可能发生!

注意:此争用不是由开发服务器模拟,只有部署在 GAE 上时才能看到,使用真实的数据存储!

会增加困惑的是事务的自动重试,这可能发生在实际写入冲突或只是简单的访问争用之后。这些重试在最终用户看来可能是某些代码路径的可疑重复执行 - 在您的案例中是处理程序。

重试实际上会使事情变得更糟(在短时间内)——在已经被频繁访问的实体组中抛出更多的访问——我见过这样的模式,只有在指数退避延迟增长到足以让事情冷静下来之后,事务才会起作用通过允许已经在进行中的事务完成来稍微(如果重试次数足够大)。

我的方法是将大部分事务性内容移到推送队列任务上,在事务和任务级别禁用重试,而是完全重新排队任务 - 重试次数更少但间隔更远。

通常当您遇到此类问题时,您必须重新访问您的数据结构和/或您访问它们的方式(您的事务)。除了保持强一致性(这可能非常昂贵)的解决方案之外,您可能需要重新检查一致性是否真的是必须的。在某些情况下,它被添加为一揽子要求只是因为似乎可以简化事情。根据我的经验,它不会:)

另一件事可以帮助(但只是一点点)是使用更快(也更昂贵)的实例类型 - 更短的执行时间转化为略低的交易重叠风险。我注意到这一点,因为我需要一个具有更多内存的实例,它恰好也更快 :)

关于python - Google App Engine 中的争用问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32927068/

相关文章:

java - Google DataStore 无主一对多关系

java - App-Engine 在调用已更新实体的 getByObjectId 时抛出 NullPointerException

python - 如何更新 NDB 模型的架构

java - 检索结果的特定子部分

python - 我如何从 gi.repository 导入 gtk.gdk

python - 当我们有 20 到 50 列时,如何在 Pandas 中创建 3 列或 4 列的 Dataframe 列表?

python - 如何让python模块兼容不同的操作系统?

python - CSV 中的列要在 python 中列出?

google-app-engine - 带服务的 App Engine 自定义域

python - 谷歌应用程序引擎 - python,迭代数据模型属性