在 Google App Engine 中,如何在处理表单提交时保持最终一致性?

In Google App Engine, how can I work with eventual consistency in handling form submissions?

我注意到,为了实现最终一致性,我习惯使用的常见表单处理工作流(提交 -> create/update 记录 -> 重定向 -> 重新加载)不起作用。重定向后,新记录(可能)将无法显示。我应该如何处理表单以便在重新加载时显示更新?

我可以尝试使用强一致性,但是作为 App Engine documentation notes, updates are limited to one update per second.

那么我如何处理提供即时用户反馈并最终保持一致的表单?

你可能仍然可以使用强一致性,只要你正确地设计你的实体组。仔细阅读链接的文档,限制实际上只有 每秒更新一次 每个实体组 .

这是因为Google的底层无主一致性协议(“Paxos”)仅在实体组(被定义为整个密钥组,在根密钥下并包括该根密钥,其中所有 non-root 密钥的最终父级是该根密钥 - 请参阅 Transactions and entity groups 了解更多信息)。

引用手册:

As mentioned above, an entity group is a set of entities connected through ancestry to a common root element. The organization of data into entity groups can limit what transactions can be performed:

... [ details about transactions ] ...

  • There is a write throughput limit of about one transaction per second within a single entity group. This limitation exists because Datastore performs masterless, synchronous replication of each entity group over a wide geographic area to provide high reliability and fault tolerance.

因此,如果您将 Datastore 密钥设计为使用 logged-in-user 的 ID 或电子邮件地址作为父密钥,则可以通过指定来保证在另一端读取生成的密钥高度一致那把钥匙作为祖先。

还值得注意的是,从技术上讲,实体不必存在于该父键中 - 您只需要一个键来启动实体组的根,以便您可以应用一致性保证。

如果你能以这种方式构建,你既可以保证一致性,也可以在 one-per-second-per-user 或你的密钥范围内应用写入。

有关详细信息,请参阅 "Structuring for Strong Consistency"

尝试重组您的代码,以便您可以通过按键获取(它始终为您提供最新数据)而不是进行查询。我知道这并不总是可行的,但我会给你一个最近对我有用的例子。

我有一个用户仪表板,用户可以在其中创建和删除 "items"。我的实体看起来像这样:

class User(ndb.Model)
    ...

class Item(ndb.Model)
    user = ndb.KeyProperty(User, required=True)

过去,我会在响应用户仪表板的 GET 请求时执行这样的查询。

items = Item.query(user=user.key)

这是一次糟糕的体验,因为用户会删除一个项目,而在 POST/redirect/GET 之后,由于最终一致性,刚刚删除的项目将再次出现在仪表板中。

为了解决这个问题,我将我的用户实体更改为具有如下所示的项目列表:

class User(ndb.Model)
    items = ndb.KeyProperty(repeated=True)
    ...

现在,当我显示仪表板时,我会这样做:

items = ndb.get_multi(user.items)

由于我现在是按键获取,所以数据总是最新的。

这对我有用,因为用户不会有那么多项目。但是,如果用户可以拥有数千个项目,由于实体大小限制,此方法将不起作用。