c# - MongoDB中upsert期间的并发问题
问题描述
我想根据具有该 id 的文档是否已经存在,在 MongoDB 中插入或更新文档。
我的对象
long _Id // Generated by myself (requirement)
List<Products> Products
所需的插入行为
检查是否存在 id X 的商店对象。如果不?插入具有给定 ID 和新产品列表的对象。
所需的更新行为
检查是否存在 id X 的商店对象?如是?通过将新项目推送到数组来更新对象。
代码
我将 C# 与 Mongo Driver 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);
}
问题
虽然这段代码一开始似乎完美地完成了这项工作,但当多次调用并行时它开始抛出一些异常:Command findAndModify failed: Non-unique id.
.
我对为什么并行执行(在此示例中为 2 个并行调用)时此(有时)失败的理论是,在两个调用(应用过滤器)中读取的集合期间,似乎没有具有给定 ID 的文档。MongoDB 决定两个查询都应该插入。第一个成功,第二个失败,Non-unique id
因为另一个查询更快并且要插入的文档已经存在(基于 id)。
FindOneAndUpdateAsync
在这种情况下,除了尝试捕获该方法并在出现重复键错误的情况下重试一次之外,没有其他(阅读:更好的)方法吗?例如,我可以在更新选项或过滤器中更改某些内容以解决此问题吗?
解决方案
除了在代码中捕获该错误并相应地处理它之外,我认为目前没有更好的方法。
您的理论是正确的,因为 2 个线程恰好同时插入。然而,这在并发系统中是预期的。
我发现解决这个问题的最佳方法是:
- 尝试插入
- 如果失败,请改为更新
这基本上是您在问题中描述的情况。
在即将发布的 MongoDB 4.2 中,这可以通过使用带有 upsert 选项的更新方法自动解决。这在SERVER-14322中有详细描述,如果可以检测到这样做是安全的,服务器将自动重试执行 upsert。
推荐阅读
- html - 如果子div没有价值,如何隐藏父div
- python - 如何使用 Anaconda 3 (Spyder) 启动 Pygubu?
- javascript - 导航栏不再滚动到页面上的部分?
- vcl - 在 Delphi 10.3 中编译 KBMMW 设计包:vcl 冲突错误 (E2199)
- reactjs - 尝试从 reducer 返回数组时应用程序崩溃
- php - Laravel:WhereBetween Date 与请求日期
- c++ - QT 和 OpenCV 问题在不同线程中的 QGraphicsView 上查看 Opencv Mat :: QObject::killTimer: 计时器不能从另一个线程停止
- vba - SAP OLE 自动化 Logon() 方法参数
- django - Django - 根据加载的视图选择选择值
- sql - 是否存在允许列和值之间视觉对称的 INSERT 语法?