Task.WhenAll fails with transaction error when awaiting multiple BulkWrite operations

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Works as Designed
    • Priority: Major - P3
    • None
    • Affects Version/s: 2.10.2
    • Component/s: None
    • None
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      When awaiting multiple BulkWriteAsync tasks using Task.WhenAll wrapped in a transaction, if no other operations have been performed on the MongoClient the transaction will fail with: Only servers in a sharded cluster can start a new transaction at the active transaction number

      For example, the following would fail:

      Unable to find source-code formatter for language: csharp. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      // ...
      // _db.RunCommand((Command<BsonDocument>)"{ping:1}");
      using (var session = await _client.StartSessionAsync().ConfigureAwait(false))
      {
          session.StartTransaction();
          var writeModel1 = new List<WriteModel<BsonDocument>>();
          writeModel1.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
          writeModel1.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
          var writeModel2 = new List<WriteModel<BsonDocument>>();
          writeModel2.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
          writeModel2.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
      
          var task1 = _collection1.BulkWriteAsync(session, writeModel1);
          var task2 = _collection2.BulkWriteAsync(session, writeModel2);
          await Task.WhenAll(task1, task2).ConfigureAwait(false);
          await session.CommitTransactionAsync().ConfigureAwait(false);
      }
      // ...
      

      but un-commenting the db operation would cause the code to run successfully.

      --------------------

      Reproduction:

      mlaunch init --replicaset --nodes 3
      mongo --eval 'db.getSiblingDB("test").collection1.insert({})'
      mongo --eval 'db.getSiblingDB("test").collection2.insert({})'
      
      Unable to find source-code formatter for language: csharp. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      using Microsoft.VisualStudio.TestTools.UnitTesting;
      using MongoDB.Bson;
      using MongoDB.Driver;
      using Shouldly;
      using System;
      using System.Collections.Generic;
      using System.Diagnostics;
      using System.Threading.Tasks;
      
      namespace MongoDBCSharpTests
      {
          [TestClass]
          public class UnitTest2
          {
              private const string connString = "mongodb://localhost:27017/test?replicaSet=replset";
              private IMongoDatabase _db;
              private MongoClient _client;
              private IMongoCollection<BsonDocument> _collection1;
              private IMongoCollection<BsonDocument> _collection2;
      
              [TestInitialize]
              public void Setup()
              {
                  _client = new MongoClient(connString);
                  _db = _client.GetDatabase("test");
                  _collection1 = _db.GetCollection<BsonDocument>("collection1");
                  _collection2 = _db.GetCollection<BsonDocument>("collection2");
              }
      
              public async Task RunTest()
              {
                  using (var session = await _client.StartSessionAsync().ConfigureAwait(false))
                  {
                      session.StartTransaction();
      
                      try
                      {
                          var writeModel1 = new List<WriteModel<BsonDocument>>();
                          writeModel1.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
                          writeModel1.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
                          var writeModel2 = new List<WriteModel<BsonDocument>>();
                          writeModel2.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
                          writeModel2.Add(new InsertOneModel<BsonDocument>(new BsonDocument { { "d", DateTime.Now } }));
      
                          var task1 = _collection1.BulkWriteAsync(session, writeModel1);
                          var task2 = _collection2.BulkWriteAsync(session, writeModel2);
                          await Task.WhenAll(task1, task2).ConfigureAwait(false);
                          await session.CommitTransactionAsync().ConfigureAwait(false);
                          Debug.WriteLine("Committed");
                      }
                      catch (Exception ex)
                      {
                          await session.AbortTransactionAsync().ConfigureAwait(false);
                          Debug.WriteLine("Error... Aborting");
                          Debug.WriteLine(ex.Message);
                          throw ex;
                      }
      
                      Debug.WriteLine("Completed");
                  }
              }
      
              [TestMethod]
              public void ShouldFailWithMongoCommandException()
              {
                  Should.Throw<MongoCommandException>(() => RunTest()).Message.ShouldContain("Only servers in a sharded cluster can start a new transaction at the active transaction number");
              }
      
              [TestMethod]
              public void ShouldSucceed()
              {
                  _db.RunCommand((Command<BsonDocument>)"{ping:1}");
                  Should.NotThrow(() => RunTest());
              }
          }
      }
      

            Assignee:
            Robert Stam
            Reporter:
            Alex Bevilacqua
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: