Search code examples
c#mongodbmongodb4.0

mongodb4.0 start a transaction in c# (drive rversion 2.7+), in the duration of transaction, the whole database will be locked, how to fix it?


(updated on 2019-7-2) 7 days after, finally, I solved this question. Well, my code is OK, WTF, wasted so much time. The question is my team use AliCloud(a chinese company, like Amazon, Azure), and the Mongodb has many versions, We created the DataBase a year ago, and that version, will lock the database while processing a trunsaction.

if you have some problem, hope my experence could help.


forgive my poor english...

dev enviroment : mongodb4.0 cluster with 3nodes, support transactions. .net core 2.2 (mongodb driver 2.7.2+) Robot 3T(mongod tool)

the MongoDB database will be locked 60 seconds after starting a transaction, I thought maybe the problem is lock level, but I've already tried all enums of readconcern or writeconvern, nothing helps.

here is an image: now database is locked, I cannot query data in Robot 3T, neither my web app.

I mean, before commitTransaction/abortTransaction, the database will be locked. if the transcation is a big one, complete in 30s, actually the webapp cannot read data response to other visitors.

in fact, the users of my webapp always say, why your app runs so slow (because every transcation lock the database for 0.xx second)

enter image description here

TransactionOptions option = new TransactionOptions(readConcern: ReadConcern.Snapshot,writeConcern: WriteConcern.W1);
ClientSessionOptions so =new ClientSessionOptions();
so.DefaultTransactionOptions = option;

var session = _dataBase.Client.StartSession();
var products = _dataBase.GetCollection<Product>("products"); 
var TV = new Product { Description = "Television", SKU = 4001, Price = 2000 };  
session.StartTransaction(option);

try
    { 
      products.InsertOne(session, TV); 
      // after the sentence , database will be locked 

// before commitTransaction, the webapp cannot response, like Robot 3T, // looks like the database is locked
session.CommitTransaction();

Solution

  • i tried to reproduce the kind of locking you're talking about with the following code. but i could not see any locking with transactions in my local environment, which uses the v2.8.1 driver and mongodb server v4.0.6. could you possibly run the following code in your environment and report back the output? also note the code uses my library MongoDB.Entities so please install that with nuget first.

    using MongoDB.Entities;
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace StackOverflow
    {
        public class Program
        {
            public class Product : Entity
            {
                public string Description { get; set; }
                public double Price { get; set; }
            }
    
            private static void Main(string[] args)
            {
                new DB("test-transacion-locking", "localhost", 27017);
    
                (new Product {
                    Description = "television",
                    Price = 399 }
                ).Save();
    
                Task.Run(() =>
                {
                    using (var TN = new Transaction())
                    {
                        var sw1 = new Stopwatch(); sw1.Start();
    
                        Console.WriteLine("transaction started...");
    
                        TN.Update<Product>()
                          .Match(p => p.Price == 399)
                          .Modify(p => p.Price, 499)
                          .Modify(p => p.Description, "updated television")
                          .Execute();
    
                        TN.Save(new Product { Description = "radio", Price = 199 }); ;
    
                        Task.Delay(10000).Wait(); //wait 10 seconds before commiting
    
                        TN.Commit();
    
                        Console.WriteLine($"transaction took: {sw1.Elapsed.TotalSeconds} seconds");
                    }
                });
    
                Task.Delay(10).Wait(); //wait 10 millis to let the transaction begin first
    
                var sw2 = new Stopwatch(); sw2.Start();
    
                Console.WriteLine("tv count query started...");
    
                var tvCount = DB.Queryable<Product>()
                                .Where(p => p.Description.Contains("television"))
                                .Count();
    
                Console.WriteLine($"found {tvCount} televisions in {sw2.Elapsed.TotalSeconds} seconds");
    
                Console.ReadKey();
            }
        }
    }
    

    this is my output:

    transaction started...
    tv count query started...
    found 1 televisions in 0.0986646 seconds
    transaction took: 10.1092237 seconds