C# Multi-Document Transaction Error

MongoDB.Driver version 2.11.5, Server version: 4.2.2-ent

I use 5 threads to execute transactions in parallel and encounter lots of 251 errors:

MongoCommandException, 251, “NoSuchTransaction”, “Command insert failed: cannot continue txnId 35 for session 618e6cd1-4db1-40ea-8b22-6386e204c36b - xxx with txnId 36”

MVP code to reproduce:

public class TransactionTest
{
    private const string DatabaseName = "PressureTest";
    private const string CollectionName = "Test";
    public const string ConnectionString = "";

    public MongoClient GetMongoClient(int timeout = 5)
    {
        var clientSettings = MongoClientSettings.FromConnectionString(ConnectionString);
        clientSettings.ConnectTimeout = TimeSpan.FromSeconds(5);
        clientSettings.ServerSelectionTimeout = TimeSpan.FromSeconds(timeout);
        clientSettings.AllowInsecureTls = true;
        var mongoClient = new MongoClient(clientSettings);

        return mongoClient;
    }

    public async Task TestTransactionAsync()
    {
        var client = GetMongoClient();
        var tasks = new List<Task>();
        for (int i = 0; i < 5; ++i)
        {
            tasks.Add(DoAsync(client));
        }

        await Task.WhenAll(tasks);
    }

    private async Task DoAsync(IMongoClient mongoClient)
    {
        Console.WriteLine(mongoClient.GetHashCode());
        while (true)
        {
            var collection = mongoClient.GetDatabase(DatabaseName).GetCollection<BsonDocument>(CollectionName);

            var uuid1 = Guid.NewGuid().ToString("N").Substring(24);
            var uuid2 = Guid.NewGuid().ToString("N").Substring(24);
            try
            {
                using (var session = await mongoClient.StartSessionAsync())
                {
                    session.StartTransaction();

                    await collection.InsertOneAsync(session, new BsonDocument("Uuid", uuid1));
                    await collection.InsertOneAsync(session, new BsonDocument("Uuid", uuid2));

                    await session.CommitTransactionAsync();
                }
                Console.WriteLine($"[{uuid1}] [{uuid2}]");
            }
            catch (Exception e)
            {
                Console.WriteLine("$$$" + e.Message);
            }
        }
    }
}

If we not reuse the mongoClient by changing TestTransactionAsync(): create a dedicated mongoClient for each thread, then no error happens:

    public async Task TestTransactionAsync()
    {
        var tasks = new List<Task>();
        for (int i = 0; i < 5; ++i)
        {
            var client = GetMongoClient(i + 5);
            tasks.Add(DoAsync(client));
        }

        await Task.WhenAll(tasks);
    }

The above modification intentionally pass different ServerSelectionTimeout value to prevent mongoclient from reusing.

Refer to: https://mongodb.github.io/mongo-csharp-driver/2.11/reference/driver/connecting/#mongo-client

multiple MongoClient instances created with the same settings will utilize the same connection pools underneath.

The document suggests re-use mongoclient by store it in a global place. However, a singleton mongoclient leads to parallel transaction failure.

What’s the right way to execute transactions in parallel?

Thanks a lot!

We are also experiencing this problem, did you managed to solve it @finisky ?

In my case, the root cause is that I created a load balancer in front of mongos. However, a transaction in sharded cluster must be executed in the same mongos instance.

For details: https://finisky.github.io/2021/01/13/dontuseloadbalancerinfrontofmongos.en/

1 Like