在 GAE 上使用 objectify 强制强一致性

Force strong consistency with objectify on GAE

我正试图在数据存储中实现强一致性,不知道有什么其他方法可以处理我的要求。我有一个用户随机相互连接的应用程序。所以基本上在 'connect' api 调用时,它会查询 QueueUser 实体,如果找不到任何实体,它会将调用用户推送到队列中。

ofy().load().type(QueueUser.class)
    .filterKey("!=", KeyFactory.createKey("QueueUser", caller.id))
    .order("__key__")
    .order("-time")
    .limit(5)
    .keys();

我知道这不会获取最新的实体键,因为索引可能不是最新的。所以我通过键进行交互并通过它的键获取每个实体。如果我得到一个非空实体,我会尝试删除它。如果我成功了,我认为这是用户匹配。这个 get-by-key 和 delete() 在 Transaction.

里面
            while(keyIterator.hasNext()) {
              QueueUser queueUser = null;
              try {
                  final Key<QueueUser> key = keyIterator.next();
                  queueUser = ofy().transactNew(1, new Work<QueueUser>() {
                      public QueueUser run() {
                          QueueUser queueUser = ofy().load().key(key).now();
                          if(queueUser == null) {
                              logger.log(Level.WARNING, "queue user was already deleted");
                              return null;
                          }
                          else
                              ofy().delete().key(key).now();
                          return queueUser;
                      }
                  });
              } catch (ConcurrentModificationException e) {
                  logger.log(Level.WARNING, "exception while deleting queue user. err: " + e.getMessage());
              }
              if (queueUser != null) {
                 // we have a match here
                 // delete calling user from the queue if it's there
                 ofy().delete().entity(new QueueUser(caller.id, null, null, null, null, null, null, null, null)).now();
                 break;
              }
          }

这在大多数情况下都有效,但有时用户会被推到队列中而没有被迅速接听。请求延迟最多可达 15 秒。查询 returns 很多实体,但大多数已被删除,一些被对等请求删除(预期为 ConcurrentModificationException)。

我想知道我是否遗漏了任何明显的东西,或者有更好的方法来处理这个问题。

这段代码完全没有意义,也没有按照您的意愿行事。对不起。我建议删除所有代码,只包括这个问题:

Am having an application where users connect to each other randomly. so basically on 'connect' api call, it queries for QueueUser entities, if it doesn't find any, it will push calling user in the queue.

假设您需要大规模执行此操作(每秒多次),这实际上很难用数据存储来完成。数据存储不喜欢快速变化的状态。您真正拥有的不是队列,而是一个位置;每当有人连接时,他们要么被放在现场(如果它是空的),要么与现场的人配对(使现场空着)。

我会使用不同的工具。为像配对这样短暂的东西存储持久状态真的没有意义。客户端无论如何都需要重试逻辑。

Memcache 可能是一个不错的选择 - 使用 getIdentifiableputIfUntouched 使其具有事务性,并结合一些重试逻辑。唯一的问题是 GAE 的 Memcache 偶尔会变得不可用(它不如数据存储可靠)。如果您正在为流行游戏构建配对引擎,您可能希望 运行 在 GCE 中使用自己的内存缓存。

老实说,构建您自己的内存服务并 运行 将其作为单例可能更容易。一个简单的版本可能是 10 行代码。