1

Intention: speed up Sql insert operation with a transaction commit / rollback support

  • Have a pretty big amount of data (about 900 k entities)
  • Would like to put down the data to SQL table in parallel, gonna to split the data per connection (e.g. 3 active connections, thus 300 k per a connection)
  • Would like to have possibility to rollback if something goes wrong, so try to using Transaction Scope (nested-TransactionScopeOption.Required)

Issue:

It looks like Transaction scope couldn't support such kind of Task.WhenAll thus, if ever there will be an error during execution the intermediate writes won't be rollback

Q: So I've wondered if it can be work around, or it just not appropriate way to leverage the transaction scope ?

   public void Upsert(IEnumerable<IEnumerable<Item>> splitPerConnection, DateTime endDate)
    {
        using (var scope = _transactionScopeFactory.Create(TransactionScopeOption.Required))
        {
            try
            {
                UpdateEndDate(endDate);

                var insertTasks = splitPerConnection.Select(ch => Task.Run(() => Insert(ch)));

                Task.WhenAll(insertTasks).GetAwaiter().GetResult();

                scope.Complete();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

    private int Insert(IEnumerable<Item> items)
    {
        int affectedRows;

        using (var scope = _transactionScopeFactory.Create(TransactionScopeOption.Required))
        using (var sqlConnection = new SqlConnection(DockerMsSqlServerConnectionString))
        {
            sqlConnection.Open();
            affectedRows = sqlConnection.Execute(ItemsQueries.Insert, items, commandTimeout: 0);

            scope.Complete();
        }

        return affectedRows;
    }

    private int UpdateEndDate(DateTime endDate)
    {
        int affectedRows;

        using (var scope = _transactionScopeFactory.Create(TransactionScopeOption.Required))
        using (var sqlConnection = new SqlConnection(DockerMsSqlServerConnectionString))
        {
            sqlConnection.Open();
            affectedRows = sqlConnection.Execute(ItemsQueries.UpdateEndDate, new { EndDate = endDate }, commandTimeout: 0);

            scope.Complete();
        }

        return affectedRows;
    }
AllmanTool
  • 1,384
  • 1
  • 16
  • 26
  • Clarification required for scenario: – Prateek Mar 17 '22 at 09:07
  • ok, how I can help ? What details you would like to know ? – AllmanTool Mar 17 '22 at 09:12
  • Clarification required for a scenario (just giving flow of the scenario): Insert Query transaction 1 Thread1 ==> Success ==> Scope complete. Insert Query transaction 2 Thread2 ==> Success ==> Scope complete. Insert Query transaction 3 Thread1 ==> Failed ==> Rollback. Will all of these transactions rolled back? – Prateek Mar 17 '22 at 09:13
  • Yes, I'm expacting that if there will be any an error in root TransactionScope (upsert method), thus I expect if the commit method (upsert method) won't be execute - the changes shouldn't be applied (write down to database) – AllmanTool Mar 17 '22 at 09:16
  • E.g 1) put breakpoint on scope.Complete() in Upsert method (root scope). 2) run the execution, 3) wait until it will be carry out and stop on the breakpoint 4) stop debugging, thus scope.Complete() won't be happened. In such scenario I expect that all writes will be rollback – AllmanTool Mar 17 '22 at 09:20
  • If you are strictly trying to try Tasks implementation, please let me know. – Prateek Mar 17 '22 at 10:01

1 Answers1

1

You can try to leverage SqlBulkCopy, instead of splitting the items over different connection objects.

Insert 2 million rows into SQL Server quickly

You can create a dataset by modifying IEnumerable<Item>. All the parallel operations can be applied here to convert from IEnumerable<Item> to dataset. Once dataset is created, SqlBulkCopy will help you. You can create 3-4 sets of datasets and execute the insert query 3-4 times.

In this approach, you can maintain one database connection that also helps to follow ACID compliance of the database transactions.

Prateek
  • 135
  • 3
  • 15