。NET中的Mongodb FindOneAndUpdate不是线程安全的吗? [重复]

问题描述 投票:1回答:1

我正在尝试创建唯一的计数器表。为了更新表中的计数器,我尝试使用FindOneAndUpdate。起初,一切似乎都按计划进行,因此我认为我会进行压力测试。我创建了一个获取计数器20,000的进程,并在5个线程上执行了该计数器。果然,我最终得到了重复。我正在针对单个本地数据库运行它;但是生产数据库是副本集。

static void Main(string[] args)
{       
    Console.WriteLine("Press Enter to begin...");
    Console.ReadLine();

        int numOfThreads = 10;
        var tasks = new List<Task>();
        for (int i = 0; i < numOfThreads; i++)            
            tasks.Add(Task.Run(() => TestWrites()));

        Console.WriteLine($"Waiting for {tasks.Count} tasks to complete...");
        Task.WaitAll(tasks.ToArray());
        Console.WriteLine("Done. Press Enter to exit.");
        Console.ReadLine();
}

private static void TestWrites()
{
    var tester = new ConcurrentTester();
    int writes = 20000;
    for (int i = 0; i < writes; i++)
        tester.InsertCounter(tester.GetCounter());

    Console.WriteLine($"Wrote {writes}");
}

public class ConcurrentTester
{
    private IMongoDatabase database;

    public ConcurrentTester()
    {
        var connStr = "";
        var mongoClient = new MongoClient(connStr);

        database = mongoClient.GetDatabase("test");
    }

    public int GetCounter()
    {
        var collection = database.GetCollection<InternalCounter>("Concurrency");
        var filter = Builders<InternalCounter>.Filter.Eq("CounterName", "Test");
        var mkt = collection.Find(filter).FirstOrDefault();

        var options = new FindOneAndUpdateOptions<InternalCounter> { IsUpsert = true, ReturnDocument = ReturnDocument.After };
        InternalCounter ctn = collection.FindOneAndUpdate(filter, 
            Builders<InternalCounter>.Update.Inc(s => s.CounterValue, 1), options);
        return ctn.CounterValue;
    }
}

public class InternalCounter
{
    public InternalCounter()
    {
        _id = ObjectId.GenerateNewId();
    }

    public object _id { get; set; }

    public string CounterName { get; set; }
    public int CounterValue { get; set; }
}

更新

由于此代码已在我们的服务中,因此我添加了一个锁来防止并发调用。在我弄清楚MongoDb中发生的事情之前,这只是一个停止的间隙。

lock (sync)
{
    ctn = collection.FindOneAndUpdate(filter,
        Builders<InternalCounter>.Update.Inc(s => s.CounterValue, 1), options);
}

更新2

我看到也有一个findAndModify方法,但是它没有在C#库中实现。从它的描述来看,它听起来应该和findOneAndUpdate一样。我还读到findOneAndUpdate取代了findAndModify。这是真的吗?

更新3经过进一步考虑,我的锁无法使用。当只有一个实例但具有负载平衡,有多个实例并且无法用锁修复时,它将起作用。

mongodb mongodb-.net-driver
1个回答
0
投票

如果您在"CounterName"字段上没有唯一索引,则最终可能会为同一计数器添加重复的行。在MongoDB docs中对此进行了描述。

© www.soinside.com 2019 - 2024. All rights reserved.