当前位置: 首页 > 面试题库 >

Google App Engine中的竞争问题

杜绍元
2023-03-14
问题内容

我在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"
    }
  >
  )

..之后是堆栈跟踪。如果需要,我可以使用整个堆栈跟踪进行更新,但这有点长。

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

# 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上时才能看到!

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

重试实际上会使情况变得更糟(短时间内)-向已经被大量访问的实体组抛出更多访问权限-
我已经看到这种模式的事务只有在指数退避延迟变得足够大以至于使事情降温后才起作用( (如果重试次数足够大)(允许已进行的交易完成)。

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

通常,当您遇到此类问题时,您必须重新访问数据结构和/或访问它们的方式(您的交易)。除了保持强一致性(可能会非常昂贵)的解决方案外,您可能还需要重新检查一致性是否是必须的。在某些情况下,将其作为一揽子要求添加是因为看起来很简单。根据我的经验,它不是:)

另一件事可以帮助(但只有一点点)正在使用更快(也更昂贵)的实例类型-
较短的执行时间可以转化为较低的事务重叠风险。我注意到了这一点,因为我需要一个具有更多内存的实例,而这个实例碰巧也更快:)



 类似资料:
  • 问题内容: (注意:这是针对MS SQL Server的) 假设您有一个具有主键标识列和CODE列的表ABC。我们希望此处的每一行都有一个唯一的,顺序生成的代码(基于一些典型的校验位公式)。 假设您有另一个仅具有一行的表DEF,该表存储下一个可用的CODE(想象一个简单的自动编号)。 我知道像下面这样的逻辑将呈现一种竞争状态,其中两个用户可能最终得到相同的CODE: 我知道,两个用户可能会卡在步骤

  • 9.1. 竞争条件 在一个线性(就是说只有一个goroutine的)的程序中,程序的执行顺序只由程序的逻辑来决定。例如,我们有一段语句序列,第一个在第二个之前(废话),以此类推。在有两个或更多goroutine的程序中,每一个goroutine内的语句也是按照既定的顺序去执行的,但是一般情况下我们没法去知道分别位于两个goroutine的事件x和y的执行顺序,x是在y之前还是之后还是同时发生是没法

  • 我在一次公司入学考试中得到了以下问题。除4个测试用例外,所有测试用例均通过。有没有人能试着找出可能出现的情况,哪些可能会失败。问题和解决方案如下: 均值、中位数和模式 给定n个整数,求其平均中值和模式。您需要填写一个接受输入整数“input1”(1)的函数 平均数和中位数必须正确到小数点后六位。 平均值:定义为数组中所有数字的平均值。 中位数:定义为数组的中间元素。 如果n是偶数,则中值是两个中间

  • 本文向大家介绍如何解决 Redis 的并发竞争 Key 问题?相关面试题,主要包含被问及如何解决 Redis 的并发竞争 Key 问题?时的应答技巧和注意事项,需要的朋友参考一下 所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同! 推荐一种方案:分布式锁(zookeeper 和 redis 都可

  • 本文向大家介绍限制竞争协议,包括了限制竞争协议的使用技巧和注意事项,需要的朋友参考一下 有限竞争协议是媒体访问控制(MAC)协议,它结合了基于冲突的协议和无冲突协议的优点。它们的行为就像轻载时的开槽ALOHA和重载时的位图协议。 概念 在计算机网络中,当一个以上的站试图通过共享信道同时进行传输时,传输的数据会出现乱码,这称为冲突。在基于冲突的协议(如ALOHA)中,允许所有站点发送帧,而无需尝试检

  • 9.6. 竞争条件检测 即使我们小心到不能再小心,但在并发程序中犯错还是太容易了。幸运的是,Go的runtime和工具链为我们装备了一个复杂但好用的动态分析工具,竞争检查器(the race detector)。 只要在go build,go run或者go test命令后面加上-race的flag,就会使编译器创建一个你的应用的“修改”版或者一个附带了能够记录所有运行期对共享变量访问工具的tes