Google app engine 在AppEngine中创建幂等客户端可检索项,给出最终一致的查询等
我需要想出一个策略来处理创建数据存储条目时的客户端重试:Google app engine 在AppEngine中创建幂等客户端可检索项,给出最终一致的查询等,google-app-engine,eventual-consistency,idempotent,Google App Engine,Eventual Consistency,Idempotent,我需要想出一个策略来处理创建数据存储条目时的客户端重试: 客户端发送在数据库中创建新条目的请求 服务器执行条目创建并准备成功回复 发生一些错误,使客户端认为请求未被处理(数据包丢失,…) 客户端再次发送相同的请求以在数据库中创建新条目 服务器检测重试并重新创建和发送原始回复,而不创建另一个数据存储条目 客户收到回复 每个人都很高兴,数据库中只创建了一个条目 我有一个限制:服务器是无状态的!它在客户端上没有任何类型的会话信息 我目前的想法如下: 用保证全局唯一的ID标记每个创建请求(以下是我
- 客户端发送在数据库中创建新条目的请求
- 服务器执行条目创建并准备成功回复
- 发生一些错误,使客户端认为请求未被处理(数据包丢失,…)
- 客户端再次发送相同的请求以在数据库中创建新条目
- 服务器检测重试并重新创建和发送原始回复,而不创建另一个数据存储条目
- 客户收到回复
- 每个人都很高兴,数据库中只创建了一个条目
- 用保证全局唯一的ID标记每个创建请求(以下是我创建它们的方式,尽管它们与问题不太相关):
- 使用数据存储(和memcache),我在每个服务器实例加载后为其分配一个唯一的、单调递增的ID(我们称之为SI)
- 当客户端请求起始页时,为请求提供服务的实例会生成一个唯一的单调递增的页面加载id(PL),并将SI.PL与页面内容一起发送给客户端
- 对于每个创建请求,客户端生成一个唯一的单调递增的请求id(RI),并将SI.PL.RI与创建请求一起发送
- 对于每个create请求,服务器首先检查是否知道create标记
- 如果没有,它将创建新条目,并以某种方式将创建标记与之一起存储
- 如果它确实知道标记,它将使用它查找最初创建的条目并重新创建相应的回复
- 当服务器收到请求时,它必须使用查询来查找任何现有条目
- 问题:因为AppEngine中的查询最终是一致的,所以它可能会丢失一个条目
- 应该可以,因为如果数字不换行,则保证是唯一的(不太可能是长的)
- 小麻烦:在将来使用时,它会增加条目键的长度(不必要的开销)
- 主要问题:这将在数据存储中生成顺序输入键,应不惜一切代价避免,因为它会显著影响性能
或者有更好的方法吗?如何为新实体分配密钥 如果您自己创建一个密钥,问题就解决了。重复实体将简单地覆盖现有实体,因为它具有相同的键。例如,创建一个产品实体,其中产品的SKU用于生成密钥 如果数据存储分配了密钥,则当请求超时时,向用户显示错误消息并将数据重新加载到客户端。然后,用户将看到是否已经创建了实体
它不像“随机序列”那么花哨,但它更简单、更可靠:)虽然可以从上面的实现选项(1)开始工作,但通过使用巧妙设计的数据结构、正确的事务和垃圾收集,让它可靠地工作将是相当痛苦的 因此,似乎正确的解决方案是采用选项(2)的广义版本: 使用所创建实体的某些唯一标识符作为实体的键。 这样,如果在重试时再次创建相同的实体,则很容易可靠地找到现有副本(因为
get
具有很强的一致性),或者盲目地再次写入该副本只会覆盖第一个版本
@Greg在评论中建议在实体的唯一标识数据上使用散列作为密钥。虽然这解决了密钥在参数空间中均匀分布的问题,从而导致数据在物理存储位置上的有效分布,但它确实产生了必须管理(或忽略)散列冲突的新问题,特别是在试图避免密钥变长的情况下
有办法处理这些碰撞。例如:在发生冲突的情况下,比较实际内容以检查它是否确实是重复的,如果不是,则在密钥中添加一个“1”。然后,查看该键是否也存在,如果存在,请再次检查其内容是否相同。如果没有,则添加一个“2”,再次检查是否有碰撞,依此类推。。。虽然这样做有效,但会变得相当混乱
或者你可以说散列冲突是如此罕见,以至于你的数据库中永远不会有足够的用户数据来查看它。我个人不喜欢这种“祈求好运”的方式,但在很多情况下,这可能是一种可以接受的方式
但是,幸运的是,我已经为数据提供了一个无冲突的全局唯一标识符:create标记。事实证明,我在使用它时看到的两个问题都可以通过一些巧妙的调整来轻松解决:
使用与原始问题中相同的标识符,我的创建标记SI.PL.RI由SI组成,它将永远增加,PL,它在每次创建新服务器实例时重置为0,以及RI为每个新客户端会话重置。因此,RI很可能是
- Lowest 10 bits of PL
- Lowest 4 bits of RI
- Lowest 17 bits of SI
- 1 bit indicating whether there are any further non-zero values
- Next lowest 10 bits of PL
- Next lowest 4 bits of RI
- Next lowest 17 bits of SI
- 1 bit indicating whether there are any further non-zero values
- ... until ALL bits of RI, PL, and SI are used (eventually breaking 10-4-17 pattern)