我需要想出一个策略来处理数据存储条目创建时的客户端重试:
- 客户端发送请求以在数据库中创建新条目
- 服务器执行条目创建并准备成功回复
- 发生一些错误,使客户端认为请求未处理(数据包丢失,...)
- 客户端发送相同的请求以再次在数据库中创建新条目
- 服务器检测到重试并重新创建并发送原始回复而不创建另一个数据存储条目
- 客户收到回复
- 每个人都很高兴,数据库中只创建了一个条目
我有一个限制:服务器是无状态的!它没有关于客户端的会话信息。
我目前的想法如下:
- 用保证的全局唯一 ID 标记每个创建请求(这是我创建它们的方式,尽管它们与问题不太相关):
- 使用数据存储(和内存缓存),我为每个服务器实例在加载后分配一个唯一的、单调递增的 ID(我们称之为SI)
- 当客户端请求起始页面时,为请求提供服务的实例会生成一个唯一的单调递增页面加载 ID ( PL ),并将SI.PL与页面内容一起发送给客户端
- 对于每个创建请求,客户端都会生成一个唯一的单调递增请求 ID ( RI ),并将SI.PL.RI与创建请求一起发送
- 对于每个创建请求,服务器首先检查它是否知道创建标签
- 如果没有,它会创建新条目并以某种方式将 create-tag 与它一起存储
- 如果它确实知道该标签,它会使用它来查找最初创建的条目并重新创建相应的回复
以下是我现在正在考虑的实施选项及其问题:
- 将 create-tag 存储为条目内的索引属性:
- 当服务器收到请求时,它必须使用查询来查找任何现有条目
- 问题:由于 AppEngine 中的查询只有最终一致,它可能会丢失一个条目
- 使用 create-tag 作为条目的键:
- 应该没问题,因为如果数字不换行,它保证是唯一的(不太可能是多头)
- 小不便:它会在将来的任何使用中增加条目密钥的长度(不必要的开销)
- 主要问题:这将在数据存储中生成顺序条目键,应不惜一切代价避免这种情况,因为它会在存储的数据中创建热点,从而显着影响性能
对于选项 2,我正在考虑的一种解决方案是使用某种公式,该公式采用序列号并将它们重新映射到一个唯一的、确定的但看起来随机的序列上,而不是消除热点。关于这样的公式可能是什么样子的任何想法?
或者也许有更好的方法?