Description
Description
I have an application that uses the TransactionScope class to control SQL transactions. We also use the same library to do mathematical simulations, but in this case it injects a dummy repository to stop the persistence to SQL. When doing this I noticed that performance became progressively worse, and eventually the app crashed with OOM errors.
I had previously created a bug report here: #1419 but it was closed because it was determined that this is a GC issue and not a TransactionScope issue.
Take this code:
using System;
using System.Diagnostics;
using System.Threading;
using System.Transactions;
namespace TestTrxScope
{
class Program
{
static void Main(string[] args)
{
int i = 0;
double lastElapsed = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
while(true)
{
using(var scope = CreateTransactionScope())
{
scope.Complete();
i++;
if(i % 1_000_000 == 0)
{
Console.WriteLine($"1m took {sw.Elapsed.TotalSeconds - lastElapsed}");
lastElapsed = sw.Elapsed.TotalSeconds;
//GC.Collect(2);
}
}
}
}
public static TransactionScope CreateTransactionScope()
{
//Memory grows constantly
return new TransactionScope(TransactionScopeOption.Suppress, new TransactionOptions() { }, TransactionScopeAsyncFlowOption.Enabled);
//This version doesn't constantly grow in memory usage
//return new TransactionScope();
}
}
}
This program's memory usage will grow forever, and the time it takes to complete 1 million TransactionScopes will also grow forever.
Uncommenting the GC.Collect(2) fixes the ever increasing memory issue, but for some reason the GC does not run on its own in this program. You might think that this is a contrived example but I found this issue in production code, where we have console apps that do extremely heavy calculations.
Note that even if GC.Collect(2) is called manually, the performance per 1 million rounds still gets worse by 75% or so until it evens out. This suggests that our data access code is impacted by this even in situations when the GC might run level 2 collections on its own due to other allocations causing it.
Configuration
.net core 3.1, Windows 10 x64
Regression?
This does not seem to happen on .net framework.
Other information
The situation improves somewhat with .net 5 as I haven't managed to get OOM errors but it still gets massively progressively worse.