Thomas Thomas - 2 months ago 22
C# Question

Concurrent changes in Async MongoDB (C#)

Let's assume I have a simple object:

class A
{
int _id;
int V1;
int V2;
}


and a MongoDB table with a bunch of these:

Now, I have two async update operations defined as such:

void UpdateV1(int id, int V)
{
var F = Builders<A>.Filter.Eq(_ => _._id, Id);
var U = Builders<A>.Update.Set(_ => _.V1, V);
Mongo.Driver.UpdateOneAsync(F, U);
}

void UpdateV2(int id, int V)
{
var F = Builders<A>.Filter.Eq(_ => _._id, Id);
var U = Builders<A>.Update.Set(_ => _.V2, V);
Mongo.Driver.UpdateOneAsync(F, U);
}


If I run the following commands:

UpdateV1(1, 10);


and

UpdateV2(1, 20);


on different threads, but roughly at the same time, what is happening?

Do I get:


  • A record where only V1 OR V2 has changed?

  • A record where both V1 AND V2 have changed?



The reason I am asking this is that we had very weird bug where it would look like the first option is what was happening, but the expected result is obviously the last one.

These problems started to disappeared when we were doing blocking calls, but it could also be a side effect.

This is using the C# driver, everything latest version.

Answer

I think this not an issue of your query because mongodb guarantee atomicity and concurrency control take a look at this link for more.

Your issue come from how tasks are scheduled I doubt. take a look at this example if you don't await for the result in Parallel.Invoke your query will not be executed because the process will exit before the tasks are executed. so if you add Console.ReadLine() your process will be blocked which allow mongo to run tasks

namespace ConsoleApplication1
{
    class Program
    {
       static MongoClient mongoClient = new MongoClient();
        static IMongoDatabase databaseBase = mongoClient.GetDatabase("dbTest", null);
        static IMongoCollection<A> collection = databaseBase.GetCollection<A>("tests", null);
        static void Main(string[] args)
        {

            var a = new A
            {
                _id = 1,
                V1 = 0,
                V2 = 0
            };

            Parallel.Invoke( ()=> { var res=UpdateV1(1, 10).Result; },  ()=> { var res =  UpdateV2(1, 20).Result; });
            //Console.ReadLine(); 




        }
        static async  Task<long> UpdateV1(int id, int V)
        {
            var F = Builders<A>.Filter.Eq(a => a._id, id);
            var U = Builders<A>.Update.Set(a => a.V1, V);
            var result = await collection.UpdateOneAsync(F, U);
            return result.ModifiedCount;  
        }

        static async Task<long> UpdateV2(int id, int V)
        {
            var F = Builders<A>.Filter.Eq(_ => _._id, id);
            var U = Builders<A>.Update.Set(_ => _.V2, V);
            var result = await collection.UpdateOneAsync(F, U);
            return result.ModifiedCount;
        }


    }
    class A
    {
       public  int _id;
       public   int V1;
       public  int V2;
    }
}

May be you have to replace UpdateOneAsync with UpdateOne only and see what's happen ?

Comments