首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实现幂等键

实现幂等键
EN

Stack Overflow用户
提问于 2022-02-25 07:56:59
回答 1查看 436关注 0票数 1

我正在尝试获得我的两个Golang端点来支持幂等键。我的服务将存储和读取来自Mongo的密钥(因为我已经将其用于其他数据)作为它自己的Collection中的唯一索引。

我正在考虑两种解决方案,但每一种都有各自的弱点。我知道还有更复杂的事情,比如保存请求和响应,以及制造逻辑酸。但是,对于我的第一个端点,only-once logic (端点的代码需要幂等)调用发送电子邮件的服务,因此不能回滚。我的第二个端点在Mongo中执行多个插入,这似乎可以被回滚,但我不确定如何以及是否有另一个解决方案也可以解决第一个端点。

解决方案1

代码语言:javascript
复制
func MyEndpoint(request Request) (Response, error) {
  doesExist, err := doesIdemKeyExist(request.IdemKey)
  if err != nil {
    return nil, status.Error(codes.Internal, "Failed to check idem key.")
  }
  if doesExist {
    return Response{}, nil
  }
  
  // < only-once logic >

  err := insertIdemKey(request.IdemKey)
  if err != nil {
    if mongo.IsDuplicateKeyError(err) {
      return Response{}, nil
    }
    return nil, status.Error(codes.Internal, "Failed to insert idem key.")
  }
 
  return Response{}, nil
}

这里的缺点是客户端可以向我的端点发送第一个请求,然后失去连接,然后用第二个请求重试。第一次请求可以处理,但不能到达insertIdemKey,第二次请求也会处理,这违反了幂等性。

解决方案2

代码语言:javascript
复制
func MyEndpoint(request Request) (Response, error) {
  err := insertIdemKey(request.IdemKey)
  if err != nil {
    if mongo.IsDuplicateKeyError(err) {
      return Response{}, nil
    }
    return nil, status.Error(codes.Internal, "Failed to insert idem key.")
  }

  // < only-once logic >

  return Response{}, nil
}

这里的缺点是,only-once logic可能会出现间歇性故障,例如依赖关系。受影响的重试请求将被忽略。

这里最好的解决方案是什么?我是否应该妥协,用这些不完美的解决方案之一呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-25 08:08:46

您应该在MongoDB中使用带有状态属性的文档,其中包含可能的值processingdone

当请求传入时,尝试使用给定的idemKeystate=processing将文档插入数据库。如果由于键已经存在而失败,则报告成功(如果状态为done)或报告仍在处理(如果状态为processing)。或者等待它完成,然后报告成功。

如果插入文档成功,继续执行“只执行一次逻辑”。

一旦完成了“仅一次逻辑”,将文档的状态更新为state=done。如果执行逻辑失败,您可以从数据库中删除文档,以便后续请求可以尝试再次执行它。

为了防止服务器在执行逻辑期间发生故障,或者防止删除文档失败,您还应该记录开始/创建时间戳,并定义过期时间。假设一个新的请求传入,并且文档存在于processing sate中,但是文档超过30秒,您可以假设它永远不会完成,并且继续下去,就像文档在数据库中不存在一样:将其创建时间戳设置为当前时间并执行逻辑,然后如果逻辑执行成功,则将状态更新为done。MongoDB也支持自动移除过期文件,但请注意删除是不精确的时间

请注意,这个解决方案也不是完美的:如果执行逻辑成功,但不能在之后将文档的状态更新为done,则过期后可能会重复执行。您需要的是逻辑和MongoDB操作的原子/事务执行,这是不可能的。

如果您的“仅一次逻辑”包含多个插入,则如果执行失败且必须重复,您可以使用insertOrUpdate()不复制记录,或者您可以插入包含idemKey的文档,以便识别先前插入的文档(可以首先删除它们,或者跳过它们,然后插入其余的文档)。

还请注意,从MongoDB 5.0开始,即支持事务,因此您可以在单个事务中执行多个插入。

见相关问题:如何通过MongoDB同步在两台不同服务器上运行的两个应用程序

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71262703

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档