question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

CancellationToken registration leak, regression

See original GitHub issue

Describe the bug

After updating to SqlClient 5.0.1, we observe a memory leak in our application. Memory dumps show a huge amount of CancellationTokenSource+CallbackNode entries (we had a dump with 2 GB of those), referencing SqlCommand objects.

We believe this might be due to the changes in:

With the changes in these PRs, it seems the CancellationToken registration is not always disposed. There are several if blocks that may return before the registration object gets to be saved in the context. And exceptions could also be thrown.

To reproduce

using Microsoft.Data.SqlClient; // 5.0.1
using System.Threading;
using System.Threading.Tasks;

public static class Program
{
    public static async Task Main()
    {
        const string query = "SELECT 1";

        // this could be the application lifetime cancellation
        // or another form of global shared cancellation
        using var longRunningCts = new CancellationTokenSource();

        var connectionString = new SqlConnectionStringBuilder
        {
            DataSource = "(local)",
            IntegratedSecurity = true,
            TrustServerCertificate = true,
        }.ToString();

        try
        {
            using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(longRunningCts.Token))
            using (var connection = new SqlConnection(connectionString))
            {
                await connection.OpenAsync(linkedCts.Token);

                using (var command = new SqlCommand(query, connection))
                using (var reader = await command.ExecuteReaderAsync(linkedCts.Token))
                {
                    linkedCts.Cancel();

                    while (await reader.ReadAsync(linkedCts.Token))
                    {

                    }
                }
            }
        }
        catch (Exception e)
        {

        }

        // => attach a debugger here
        // linkedCts as well as all the SQL objects have been disposed above
        // however a memory snapshot shows that the CancellationTokenSource+Registrations and CancellationTokenSource+CallbackNode are still there, including the objects they reference
        // so garbage collection cannot happen correctly
    }
}

Expected behavior

No memory leak, CancellationTokenSource+CallbackNode entries are not present in a dump.

Further technical details

Microsoft.Data.SqlClient version: 5.0.1 .NET target: .NET 6.0.3 SQL Server version: SQL Server 2019 Operating system: Windows 2019

Additional context

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:17
  • Comments:34 (15 by maintainers)

github_iconTop GitHub Comments

5reactions
aarondandycommented, Jan 6, 2023

I think many out there (like me) who took way too long to discover this as the root cause are going to really appreciate upgrading to 5.1.0 when it is released. image

5reactions
Shane32commented, Nov 1, 2022

I’m having this issue also. After moving to 5.0.1 my Linux server would leak memory rapidly until crashing. Analyzing a memory dump revealed millions of instances of SqlCommand rooted only by the cancellation token registration (CallbackNode). Even after just a couple hours, I can confirm that reverting to 5.0.0 has solved the problem. For an in-depth analysis, including dump screen shots, system configuration, and other information, please see this issue:

image

(Azure App Service - Linux - Memory Working Set - last 30 days)

image

(Azure App Service - Linux - Memory Working Set - last 3 days)

I recommend marking 5.0.1 as unstable on NuGet for three reasons:

  1. This bug potentially affects anyone that has SQL code continuously or periodically executing within a BackgroundService, which is probably common enough.
  2. The bug is not necessarily obvious (if the memory leak is small), and is very difficult to track down and diagnose, and even harder to write a test for. (I spent probably 40 hours on this.)
  3. The severity of the bug is such that it can often result in the application crashing. Not only that, it can crash other applications within the same server/VM. (For me, it affected all web apps within within same Azure App Service Plan.)
Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - Why is my async/await with CancellationTokenSource ...
According to the profiler, those RestResponse objects are being kept alive because they're referred to by the TaskCompletionSource (via the ...
Read more >
1784707 – [Regression] [Memory leak] Has unclosed fd
This is expected and a bug in nmstate. Hold on why: When you create an NMClient instance (from after calling either g_initiable_init() or ......
Read more >
How to Avoid Data Leakage When Performing Data ...
A careful application of data preparation techniques is required in order to avoid data leakage, and this varies depending on the model ...
Read more >
How To Avoid Memory Leaks In JavaScript
In this article, we look at what memory leaks are, their causes, and how to avoid memory leaks in JavaScript.
Read more >
Data Leakage And Its Effect On The Performance of An ML ...
Data Leakage is the scenario where the Machine Learning Model is already aware of some part of test data after training.This causes the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found