Why this code create an exception?

Hi,

I’m trying to use mongodb to have a multi-process lock system. I use FindOneAndUpdate atomicity for that.
This is not to be use for high speed lock, I use this sometime in my code.
I’m using the latest C# driver from Github and mongodb for Windows version v3.6.10.

Sometime I get exception “E11000 duplicate key error collection”.
It’s quite easy to reproduce, run the process 2 or 3 time to get it.

Why is it possible to get this exception ?
How to fix this ?

Remi

Process 88 have the lock
Process 60 have the lock
One or more errors occurred.
Command findAndModify failed: E11000 duplicate key error collection: mq.lock index: id dup key: { : ObjectId(‘632d4d5ed0371a56dcdd187a’) }.
Average = 200,778591919192, Min = 59,0069, Max = 313,0022
Press return to exit.

static void Main(string[] args)
{
    var cnx = new MongoClient();
    var db = cnx.GetDatabase("mq");
    db.DropCollection("lock");
    var col = db.GetCollection<BsonDocument>("lock");

    ObjectId id = ObjectId.GenerateNewId();
    BsonDocument t = new BsonDocument().Add("hello", "world");
    Random rnd = new Random();
    var fb = Builders<BsonDocument>.Filter;
    var ub = Builders<BsonDocument>.Update;
    var ud = ub.CurrentDate("dt").Inc("run", 1).SetOnInsert("data", t);

    List<Task> tasks = new List<Task>();
    List<double> mean = new List<double>();

    for (int i = 0; i < 100; i++)
    {
        int j = i;
        tasks.Add(Task.Run(async () =>
        {
            await Task.Delay(rnd.Next(10, 100));
            DateTime start = DateTime.Now;
            BsonDocument d = await col.FindOneAndUpdateAsync(
                fb.Eq("_id", id),
                ud,
                new FindOneAndUpdateOptions<BsonDocument>()
                    { IsUpsert = true, ReturnDocument = ReturnDocument.After }
                );
            mean.Add((DateTime.Now - start).TotalMilliseconds);

            if (d["run"].AsInt32 == 1)
            {
                Console.WriteLine($"Process {j} have the lock");
                await Task.Delay(rnd.Next(10, 100));
                await col.DeleteOneAsync(fb.Eq("_id", id));
            }
        }));
    }
    try
    {
        Task.WaitAll(tasks.ToArray());
    }
    catch (Exception ex)
    {
        do
        {
            Console.WriteLine(ex.Message);
            ex = ex.InnerException;
        }
        while (ex != null);
    }

    Console.WriteLine($"Average = {mean.Average()}, Min = {mean.Min()}, Max = {mean.Max()}");
    Console.WriteLine("Press return to exit.");
    Console.ReadLine();
}

I reply to myself.

The problem is the version of mongodb engine I have to use.
With a more recent version I don’t have the problem.

Remi