Details
-
Bug
-
Resolution: Works as Designed
-
Major - P3
-
None
-
2.11.5
-
None
-
MongoDB Sharded Cluster version: 4.2.2-ent
OS: Ubuntu 16.04
Description
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"
Reproduce code:
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) |
{
|
//var client = GetMongoClient(i + 5); |
tasks.Add(DoAsync(client));
|
}
|
await Task.WhenAll(tasks);
|
}
|
|
|
private async Task DoAsync(IMongoClient mongoClient) |
{
|
Console.WriteLine("Client hashcode: " + mongoClient.GetHashCode()); |
var collection = mongoClient.GetDatabase(DatabaseName).GetCollection<BsonDocument>(CollectionName);
|
|
|
while (true) |
{
|
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 change the thread to 1, no error happens.
If not reuse the mongoClient by changing TestTransactionAsync(): create a dedicated mongoClient for each thread, 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 passes 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.