C# MongoDB中upsert期间的并发问题

C# MongoDB中upsert期间的并发问题,c#,mongodb,C#,Mongodb,我想在MongoDB中插入或更新一个文档,基于该id的文档是否已经存在 我的对象 long _Id // Generated by myself (requirement) List<Products> Products long\u Id//由我自己生成(需求) 列出产品 所需的插入行为 检查id为X的店铺对象是否存在。如果没有?插入具有给定id和新产品列表的对象 所需的更新行为 检查id为X的店铺对象是否存在?如果有的话?通过将新项推送到数组来

我想在MongoDB中插入或更新一个文档,基于该id的文档是否已经存在

我的对象

long _Id                 // Generated by myself (requirement)
List<Products> Products
long\u Id//由我自己生成(需求)
列出产品
所需的插入行为

检查id为X的店铺对象是否存在。如果没有?插入具有给定id和新产品列表的对象

所需的更新行为

检查id为X的店铺对象是否存在?如果有的话?通过将新项推送到数组来更新对象

代码

我将C#与Mongo驱动程序Nuget包一起使用。我想出了以下代码:

public async Task<MongoCmdResult> CreateOrUpdate(ShopDocument shopDocument)
{
    var filterLibrary    = Builders<ShopDocument>.Filter;
    var filter           = filterLibrary.Eq(shop => shop._Id, shopDocument._Id)
            & filterLibrary.ElemMatch(shop => shop.Products, products => products.ProductId != shopDocument.Products[0].ProductId);
    var updateDefinition = Builders<ShopDocument>.Update.Push<ShopDocument.Products>(shop => shop.Products, shopDocument.Products[0]);
    var updateOptions    = new FindOneAndUpdateOptions<ShopDocument> { IsUpsert = true };
    return await collection.FindOneAndUpdateAsync(filter, updateDefinition, updateOptions);
}
公共异步任务CreateOrUpdate(ShopDocument ShopDocument)
{
var filterLibrary=Builders.Filter;
var filter=filterLibrary.Eq(shop=>shop.\u Id,shopDocument.\u Id)
&filterLibrary.ElemMatch(shop=>shop.Products,Products=>Products.ProductId!=shopDocument.Products[0].ProductId);
var updateDefinition=Builders.Update.Push(shop=>shop.Products,shopDocument.Products[0]);
var updateOptions=new FindOneAndUpdateOptions{IsUpsert=true};
return wait collection.FindOneAndUpdateAsync(filter、updateDefinition、updateOptions);
}
问题

虽然这段代码起初似乎做得很好,但在多次并行调用时,它开始抛出一些异常:
命令findAndModify failed:Non-unique id.

我的理论是,在并行执行时(在本例中为2个并行调用),这(有时)失败的原因是,在两个调用中读取的集合(应用过滤器)中似乎没有具有给定id的文档。MongoDB决定两个查询都应插入。第一个查询成功,第二个查询在
非唯一id
上失败,原因是另一个查询更快,并且要插入的文档已经存在(基于id)


在这个场景中,除了尝试捕获
FindOneAndUpdateAsync
方法并在出现重复密钥错误时重试一次之外,没有其他(读:更好)的方法了吗?例如,我是否可以在更新选项或过滤器中更改某些内容来解决此问题?

我认为目前没有更好的方法,只能捕获错误并在代码中相应地处理它

你的理论是正确的,因为两个线程恰好同时插入。然而,在并发系统中这是一种预期

我发现解决这一问题的最佳方法是:

  • 试着插入
  • 如果失败,请改为执行更新
  • 这基本上就是你在问题中描述的情况

    在即将发布的MongoDB 4.2中,这可以通过使用自动解决。这在中有详细描述,其中服务器将在检测到安全的情况下自动重试执行upsert