Почему мой код драйвера mongo .net не безопасен для параллелизма? - PullRequest
2 голосов
/ 11 апреля 2019

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

Я не всегда вижу 100 дюймов в моем документе в базе данных Монго ...
Что-то интересное, когда я проверяю стек монго, я вижу только 93 обновления вызовов монго и 93 целых в моей коллекции монго. Поэтому я делаю вывод, что проблема в драйвере.

Монго стека:

   
{ 
"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 голосов
/ 30 апреля 2019

Хорошо, я наконец дошел до сути.Проблема связана исключительно с c # и возникла из-за использования синтаксиса async / await внутри блока Parallel.For.

Замена кода на приведенный ниже исправила проблему.

[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 и assert привело к тому, что данные вообще не были вставлены в Mongo.

Для получения дополнительной информации об этом см. В Parallel.ForEach ожидают вложения.

Спасибо тем, кто это исследовал, и приносит извинения за неудобства.

...