为什么我的 mongo .net 驱动程序代码不是并发安全的



WiredTiger应该处理文档级并发。
Mongo .Net 驱动程序是否与此锁定机制兼容?

为什么这段代码没有给我正确的结果:

public class MyClass
{
    public int Id { get; set; } = 1;
    public List<int> Prop { get; set; }
}
public class MyClassRepository : Repository<int, MyClass>
{    
    public async Task TestUpdate(MyClass document, int val)
    {
        var filter = Builders<MyClass>.Filter.Eq("_id", document.Id);
        var update = Builders<MyClass>.Update.Push(_ => _.Prop, val);
        await this.Collection.UpdateOneAsync(filter, update);
    }
}
[Fact]
public async Task Test2()
{
    var myClass = new MyClass { Prop = new List<int>() };
    Parallel.For(
        0,
        100,
        async i =>
        {
            await this._repository.TestUpdate(myClass, i);
        });
    var result = await this._repository.Get(1);
    result.Prop.Count.Should().Be(100);
}

我并不总是在 mongo 数据库中的文档中看到 100 个整数......
有趣的是,当我检查 mongo 堆栈时,我只看到 93 个对 mongo 的更新调用和 93 个 mongo 集合中的 int。因此,我的结论是司机是问题所在。

蒙戈堆栈 :

   
{ 
"op" : "update", 
"ns" : "auditing.myclasses", 
"command" : {
    "q" : {
        "_id" : NumberInt(1)
    }, 
    "u" : {
        "$push" : {
            "Prop" : NumberInt(42)
        }
    }, 
    "multi" : false, 
    "upsert" : false
}, 
"keysExamined" : NumberInt(1), 
"docsExamined" : NumberInt(1), 
"nMatched" : NumberInt(1), 
"nModified" : NumberInt(1), 
"numYield" : NumberInt(0), 
"locks" : {
    "Global" : {
        "acquireCount" : {
            "r" : NumberLong(1), 
            "w" : NumberLong(1)
        }
    }, 
    "Database" : {
        "acquireCount" : {
            "w" : NumberLong(1)
        }
    }, 
    "Collection" : {
        "acquireCount" : {
            "w" : NumberLong(1)
        }
    }
}, 
"millis" : NumberInt(0), 
"planSummary" : "IDHACK", 
"execStats" : {
    "stage" : "UPDATE", 
    "nReturned" : NumberInt(0), 
    "executionTimeMillisEstimate" : NumberInt(0), 
    "works" : NumberInt(2), 
    "advanced" : NumberInt(0), 
    "needTime" : NumberInt(1), 
    "needYield" : NumberInt(0), 
    "saveState" : NumberInt(0), 
    "restoreState" : NumberInt(0), 
    "isEOF" : NumberInt(1), 
    "invalidates" : NumberInt(0), 
    "nMatched" : NumberInt(1), 
    "nWouldModify" : NumberInt(1), 
    "nInvalidateSkips" : NumberInt(0), 
    "wouldInsert" : false, 
    "fastmodinsert" : false, 
    "inputStage" : {
        "stage" : "IDHACK", 
        "nReturned" : NumberInt(1), 
        "executionTimeMillisEstimate" : NumberInt(0), 
        "works" : NumberInt(1), 
        "advanced" : NumberInt(1), 
        "needTime" : NumberInt(0), 
        "needYield" : NumberInt(0), 
        "saveState" : NumberInt(1), 
        "restoreState" : NumberInt(1), 
        "isEOF" : NumberInt(1), 
        "invalidates" : NumberInt(0), 
        "keysExamined" : NumberInt(1), 
        "docsExamined" : NumberInt(1)
    }
}, 
"ts" : ISODate("2019-04-12T11:46:59.118+0000"), 
"client" : "127.0.0.1", 
"appName" : "Auditing", 
"allUsers" : [
], 
"user" : ""
}    

好的,我终于明白了。问题纯粹与 c# 相关,来自在 Parallel.For 块中使用 async/await 语法。

用下面的代码替换代码解决了这个问题。

[Fact]
public async Task Test2()
{
    var myClass = new MyClass { Prop = new List<int>() };
    var tasks = new List<Task>();
    for (var i = 0; i < 100; i++)
    {
        tasks.Add(this._repository.TestUpdate(myClass, i));
    }
    await Task.WhenAll(tasks);
    var result = await this._repository.Get(1);
    result.Prop.Count.Should().Be(100);
}

让我得出这个结论的是,注释 Get 和断言部分导致根本没有在 Mongo 中插入任何数据。

有关此内容的更多信息,请参阅Nesting await in Parallel.ForEach

感谢那些对此进行调查的人,对于给您带来的不便,我们深表歉意。

最新更新