在我使用query.Count()验证之前,Mongo写了query.UpsertId多个文档。

时间:2018-03-27 08:50:11

标签: mongodb go mgo

说明:我在我的项目中使用mongoDB。当用户试图将他的物品出售时,这是处理程序的简短逻辑。在向mongo提供优惠之前,我验证了优惠,因此没有有效的优惠与save assetId

使用:

  • mgo.v2

  • mongo 3.6

  • golang 1.10

问题:如果用户点击真的很快就会向我的处理程序发送多个请求(假设他快速双击鼠标),验证不起作用,因为看起来第一个报价不在该集合尚未获得,因此我获得了2-3个具有相同assetId的优惠。

我试过

  • 设置mongoUrl?replicaSet = rs0,所以我们的主人和奴隶现在互相约会
  • 验证后设置time.Sleep(200 * time.Millisecond)

问题:

我有什么办法可以用mongo乐器处理这个问题,或者有人建议其他一些解决方法吗?

提前谢谢!

S

更新

我试图更多地重现这个问题。所以我写了这个小测试代码,因此在验证(count> 0)工作之前设法编写了60个文档。这个例子完全重现了我的问题。

checkIfContainsOnlyParenthesis(S);

2 个答案:

答案 0 :(得分:4)

首先是在呼叫进行时禁用客户端的“发送”按钮,因此如果用户双击或三次点击,则无效,因为第二次和后续呼叫将以禁用按钮为目标因此什么都不会发生。

如果同一个订单可能来自您想要多次保存的多个地方,这已经足够了,并且是正确的方法。

如果ID也来自客户端,并且如果只有一个订单可能存在给定ID,那么接下来您应该只使用订单ID作为MongoDB中的文档ID:分配并使用此值作为MongoDB _id字段。这将保证不存在具有相同订单ID的多个商品,第二次尝试插入订单将返回错误。请注意,使用Query.UpsertId()将始终成功,插入文档(如果不存在),并更新(如果不存在)。如果文档不存在,则Query.Insert()插入文档,如果文档已经存在,则返回错误。不使用UpsertId()Insert()将导致多个文档具有相同的ID。

如果由于某种原因您不能或不想将订单ID用作文档ID,则为存储订单ID的属性定义唯一索引,有关详细信息,请参阅MongoDB Unique Indexes。< / p>

请注意,使用MongoDB _id字段或其他具有唯一索引的字段本身可确保您无法插入具有相同订单ID的多个文档(由MongoDB确保)。另请注意,即使您有一个包含多个MongoDB实例的集群,这也会起作用,因为写入(包括插入)总是发生在主节点上。因此,在多服务器群集环境中无需任何其他操作。

答案 1 :(得分:0)

最终,经过对bug的仔细调查,我们发现原因是当用户发送请求时,它是在goroutine中处理的。意味着很多请求=很多并发goroutines。因此,它验证器(检查是否在收集中),无法找到它,因为它还没有在mongo中。所以,最后,我们决定使用redis作为验证器。

这是简短的实施:

incr, err := redisClient.Incr(offer.AssetId).Result()
    if err != nil {
        return err
    }

    if incr > 1 {
        return errors.New("ASSET_ALREADY_ON_SALE")
    }

    redisClient.Expire(offer.AssetId, time.Second*10)

希望它能帮助遇到同样问题的人。

Link on implementation description: