Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: RepoDb.SqlServer 1.13.0 - Memory leak in Microsoft.Data.SqlClient 5.0.1 #1128

Closed
paaaz opened this issue Jan 17, 2023 · 2 comments
Closed
Assignees
Labels
bug Something isn't working fixed The bug, issue, incident has been fixed.

Comments

@paaaz
Copy link

paaaz commented Jan 17, 2023

Bug Description

Unfortunately Microsoft.Data.SqlClient in version 5.0.1 has a severe memory leak, which also leads to leaking memory using RepoDb.SqlServer v1.13.0.
Details: Issue #1810 on SqlClient Github repo

Reproduction Steps

Use RepoDb Async calls in a periodic background service adapted from code example dotnet/SqlClient#1810 (comment)

using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Data.SqlClient;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Hosting;
    using RepoDb;

    public class MemLeakingService : BackgroundService
    {
        private readonly string connectionString;

        public MemLeakingService(IConfiguration configuration)
        {
            this.connectionString = configuration.GetConnectionString("Database");
        }

        protected override async Task ExecuteAsync(CancellationToken token)
        {
            Console.WriteLine($"SqlClient version: {FileVersionInfo.GetVersionInfo(typeof(SqlCommand).Assembly.Location).ProductVersion}");

            while (true)
            {
                const string sql = "SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n)";
                using (var sqlConnection = new SqlConnection(this.connectionString))
                {
                    await sqlConnection.ExecuteQueryAsync(sql, cancellationToken: token).ConfigureAwait(false);
                }

                GC.Collect(); // for demonstration only; removing this line does not change output
                Console.WriteLine($"Lost registrations: {CountLostSqlCommands(token)}");
            }
        }

        static int CountLostSqlCommands(CancellationToken token)
        {
            var cts = (CancellationTokenSource)typeof(CancellationToken).GetField("_source", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!.GetValue(token)!;
            var registrationsField = typeof(CancellationTokenSource).GetField("_registrations", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!;
            var registrations = registrationsField.GetValue(cts);
            if (registrations == null)
                return 0;
            var callbacksProperty = registrationsField.FieldType.GetField("Callbacks", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)!;
            var callbackNode = callbacksProperty.GetValue(registrations);
            var callbackNodeType = callbacksProperty.FieldType;
            var nextProperty = callbackNodeType.GetField("Next", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)!;
            var stateProperty = callbackNodeType.GetField("CallbackState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)!;
            int lostSqlCommands = 0;
            while (callbackNode != null)
            {
                var callbackState = stateProperty.GetValue(callbackNode);
                if (typeof(SqlCommand) == callbackState?.GetType())
                {
                    lostSqlCommands += 1;
                }
                callbackNode = nextProperty.GetValue(callbackNode);
            }
            return lostSqlCommands;
        }
    }

Resolution

Update Microsoft.Data.SqlClient to 5.1.0 as soon as the preview version is marked as stable (should be soon)

Library Version:

RepoDb v1.13.0 and RepoDb.SqlServer v1.13.0

@paaaz paaaz added the bug Something isn't working label Jan 17, 2023
@paaaz
Copy link
Author

paaaz commented Jan 23, 2023

Version 5.1.0 of Microsoft.Data.SqlClient is released: https://github.com/dotnet/SqlClient/releases/tag/v5.1.0

SergerGood added a commit that referenced this issue Mar 9, 2023
mikependon added a commit that referenced this issue Mar 10, 2023
#1128 Bug: RepoDb.SqlServer 1.13.0 - Memory leak in Microsoft.Data.SqlClient 5.0.1
@mikependon mikependon pinned this issue Mar 16, 2023
@mikependon mikependon unpinned this issue Mar 16, 2023
@mikependon mikependon added the fixed The bug, issue, incident has been fixed. label Mar 16, 2023
@mikependon
Copy link
Owner

The fix has just been released to this. Please use the v1.13.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed The bug, issue, incident has been fixed.
Projects
None yet
Development

No branches or pull requests

2 participants