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

时间:2019-04-11 20:35:02

标签: mongodb concurrency mongodb-.net-driver

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堆栈时,我在mongo集合中仅看到93个对mongo的更新调用和93个整数。因此,我认为驱动程序是问题。

Mongo堆栈:

   
{ 
"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" : ""
}    

1 个答案:

答案 0 :(得分:0)

好吧,我终于明白了这一点。问题完全与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 and assert部分导致根本没有在Mongo中插入任何数据。

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

感谢调查此事的人士,不便之处,敬请谅解。