2

WiredTiger is supposed to handle document level concurrency.
Is the Mongo .Net Driver compatible with this locking mechanism ?

Why is this code not giving me the right result :

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);
}

I don't always see 100 ints in my document in mongo database...
Something interesting is, when I check the mongo stack, i see only 93 update calls to mongo and 93 ints in my mongo collection. Therefore, I'm concluding that the driver is the issue.

Mongo stack :

   
{ 
"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" : ""
}    
  • The queries that come from this, what do they look like? – Sergio Tulentsev Apr 11 '19 at 21:31
  • (it's been a while since I touched C# and I'm a little confused by what's happening in the update section. This is the [Update.Push](https://mongodb.github.io/mongo-csharp-driver/2.7/apidocs/html/M_MongoDB_Driver_Builders_Update_Push.htm), I'm assuming? But it accepts a string and not a lambda. It's also very possible that I'm misreading the syntax here) – Sergio Tulentsev Apr 11 '19 at 21:33
  • see snippet above – Benjamin Lalonde Apr 12 '19 at 12:05
  • Created an issue https://jira.mongodb.org/browse/CSHARP-2584. Let's see what is the outcome about this. – Benjamin Lalonde Apr 12 '19 at 15:05

1 Answers1

0

Ok, I finally got to the bottom of this. Issue is purely c# related and came from using async/await syntax inside the Parallel.For block.

Replacing the code with the one below fixed the issue.

[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);
}

What brought me to this conclusion is that commenting the Get and assert part resulted in no data being inserted in Mongo at all.

For more info about this see Nesting await in Parallel.ForEach

Thanks to those who investigated this and sorry for the inconvenience.