Open Closed

ResetPassword flow SQL query idle in connection pool #9904


User avatar
1
shodgson created

We ran into an issue in our hosted environment where multiple users calling /Account/ResetPassword caused SQL connections to stay idle in the connection pool instead of being released.

During user onboarding, we require all users of a tenant to reset their password. After a recent onboarding, we saw a spike in usage of the Identity and Authentication server. Both share the Identity database connection string (from the ABP microservice template).

Over a period of time, the session count on the Identity database kept increasing without ever going down. This build-up eventually exhausted the connection pool and broke our app until we restarted it.

What we saw:

Every call to /Account/ResetPassword runs a query that gets translated to SQL like this:

Even under low usage, sessions used by this query remain idle for extended periods instead of being released back to the pool.

Impact:

  • Multiple calls to /Account/ResetPassword cause sessions to accumulate.
  • Eventually, the Authentication server cannot obtain a connection because the pool is full.

Repro steps: I was able to reproduce this locally on SQL Server by:

  • Setting a fixed Max Pool Size in the Identity database connection string (to simulate our hosted DB limits).
  • Running a simple load test with k6 or another tool against GET /Account/ResetPassword.
  • Observing the database sessions steadily increase without decreasing.
  • Eventually hitting the same error we saw in our hosted environment:
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. 
This may have occurred because all pooled connections were in use and max pool size was reached.

Is there a way to prevent this issue without having to raise our database limits?

thanks


14 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • The issue of SQL connections remaining idle and not being released in the connection pool during high usage of /Account/ResetPassword is typically related to improper disposal of DbContext or uncompleted Unit of Work (UoW) scopes in ABP-based applications.

    • In ABP, database connections are managed by the Unit of Work system. If a UoW is not completed or disposed properly (for example, due to an unawaited async call, an exception, or a long-running operation that never finishes), the underlying DbContext and its SQL connection may remain open, causing connection pool exhaustion.

    • To prevent this issue:

      1. Ensure all application service and controller methods that interact with the database are async and properly awaited.
      2. Never block on async code (e.g., avoid .Result or .Wait() on async methods).
      3. Always complete or dispose the Unit of Work. In ABP, this is handled automatically for application services and controllers, but custom UoW usage (e.g., IUnitOfWorkManager.Begin()) must be completed with await uow.CompleteAsync().
      4. Check for any custom middleware, filters, or background jobs that might be holding onto DbContext or UoW instances longer than necessary.
      5. Review your code for any exceptions that might prevent UoW completion, especially in the password reset flow.
    • For more details, see:

    • If you have custom code in your password reset logic (such as custom UoW or DbContext usage), ensure it follows the above best practices. If the problem persists, consider enabling detailed logging for UoW and DbContext lifetimes to identify where connections are not being released.

    Sources:

    • https://abp.io/docs/latest/Entity-Framework-Core#connection-management
    • https://abp.io/docs/latest/Unit-Of-Work
    • https://abp.io/support/questions/1326

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    shodgson created

    That query seems to be initiated within the ABP codebase so we don't have an easy control over it

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    GET /Account/ResetPassword.

    Can you share the curl info of this request?


    I checked the code, and it just queries the user and updates the password. No other operations.

    It should not have any database problems.


    Can you enable EF Core Debug logs and share the logs.txt file?

    var loggerConfiguration = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Debug)
        .Enrich.FromLogContext()
        .WriteTo.Async(c => c.File("Logs/logs.txt"))
    

    liming.ma@volosoft.com

    See https://abp.io/support/questions/8622/How-to-enable-Debug-logs-for-troubleshoot-problems

    Thanks.

  • User Avatar
    0
    shodgson created

    Log traces sent to your mail address,

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Thanks. I will check your logs.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you also share the source code of FortisIaResetPasswordModel(FortisIA.AuthServer.Pages.Account.FortisIaResetPasswordModel)?

    Thanks.

  • User Avatar
    0
    shodgson created

    Hello,

    The code has been sent to your email.

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Thanks. I will check it.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you try setting includeDetails to false during ResetPasswordAsync and then try again?

    var user = await IdentityUserRepository.GetAsync(input.UserId, includeDetails: false);

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.Extensions.Options;
    using Volo.Abp;
    using Volo.Abp.Account;
    using Volo.Abp.Account.Emailing;
    using Volo.Abp.Account.PhoneNumber;
    using Volo.Abp.BlobStoring;
    using Volo.Abp.Caching;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.Identity;
    using Volo.Abp.Imaging;
    using Volo.Abp.SettingManagement;
    
    namespace MyCompanyName.MyProjectName;
    
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AccountAppService), typeof(IAccountAppService))]
    public class MyAccountAppService : AccountAppService
    {
        protected IIdentityUserRepository IdentityUserRepository { get; set; }
    
        public MyAccountAppService(
            IdentityUserManager userManager,
            IAccountEmailer accountEmailer,
            IAccountPhoneService phoneService,
            IIdentityRoleRepository roleRepository,
            IdentitySecurityLogManager identitySecurityLogManager,
            IBlobContainer<AccountProfilePictureContainer> accountProfilePictureContainer,
            ISettingManager settingManager,
            IOptions<IdentityOptions> identityOptions,
            IIdentitySecurityLogRepository securityLogRepository,
            IImageCompressor imageCompressor,
            IOptions<AbpProfilePictureOptions> profilePictureOptions,
            IApplicationInfoAccessor applicationInfoAccessor,
            IdentityUserTwoFactorChecker identityUserTwoFactorChecker,
            IDistributedCache<EmailConfirmationCodeCacheItem> emailConfirmationCodeCache,
            IdentityErrorDescriber identityErrorDescriber,
            IOptions<AbpRegisterEmailConfirmationCodeOptions> registerEmailConfirmationCodeOptions,
            IIdentityUserRepository identityUserRepository)
            : base(userManager, accountEmailer, phoneService, roleRepository, identitySecurityLogManager,
                accountProfilePictureContainer, settingManager, identityOptions, securityLogRepository, imageCompressor,
                profilePictureOptions, applicationInfoAccessor, identityUserTwoFactorChecker, emailConfirmationCodeCache,
                identityErrorDescriber, registerEmailConfirmationCodeOptions)
        {
            IdentityUserRepository = identityUserRepository;
        }
    
        public override async Task ResetPasswordAsync(ResetPasswordDto input)
        {
            await IdentityOptions.SetAsync();
            var user = await IdentityUserRepository.GetAsync(input.UserId, includeDetails: false);
            (await UserManager.ResetPasswordAsync(user, input.ResetToken, input.Password)).CheckErrors();
    
            await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
            {
                Identity = IdentitySecurityLogIdentityConsts.Identity,
                Action = IdentitySecurityLogActionConsts.ChangePassword
            });
        }
    }
    
    

    Thanks

  • User Avatar
    0
    shodgson created

    [maliming] said: hi

    Can you try setting includeDetails to false during ResetPasswordAsync and then try again?

    var user = await IdentityUserRepository.GetAsync(input.UserId, includeDetails: false);

    using System.Threading.Tasks; 
    using Microsoft.AspNetCore.Identity; 
    using Microsoft.Extensions.Options; 
    using Volo.Abp; 
    using Volo.Abp.Account; 
    using Volo.Abp.Account.Emailing; 
    using Volo.Abp.Account.PhoneNumber; 
    using Volo.Abp.BlobStoring; 
    using Volo.Abp.Caching; 
    using Volo.Abp.DependencyInjection; 
    using Volo.Abp.Identity; 
    using Volo.Abp.Imaging; 
    using Volo.Abp.SettingManagement; 
     
    namespace MyCompanyName.MyProjectName; 
     
    [Dependency(ReplaceServices = true)] 
    [ExposeServices(typeof(AccountAppService), typeof(IAccountAppService))] 
    public class MyAccountAppService : AccountAppService 
    { 
        protected IIdentityUserRepository IdentityUserRepository { get; set; } 
     
        public MyAccountAppService( 
            IdentityUserManager userManager, 
            IAccountEmailer accountEmailer, 
            IAccountPhoneService phoneService, 
            IIdentityRoleRepository roleRepository, 
            IdentitySecurityLogManager identitySecurityLogManager, 
            IBlobContainer<AccountProfilePictureContainer> accountProfilePictureContainer, 
            ISettingManager settingManager, 
            IOptions<IdentityOptions> identityOptions, 
            IIdentitySecurityLogRepository securityLogRepository, 
            IImageCompressor imageCompressor, 
            IOptions<AbpProfilePictureOptions> profilePictureOptions, 
            IApplicationInfoAccessor applicationInfoAccessor, 
            IdentityUserTwoFactorChecker identityUserTwoFactorChecker, 
            IDistributedCache<EmailConfirmationCodeCacheItem> emailConfirmationCodeCache, 
            IdentityErrorDescriber identityErrorDescriber, 
            IOptions<AbpRegisterEmailConfirmationCodeOptions> registerEmailConfirmationCodeOptions, 
            IIdentityUserRepository identityUserRepository) 
            : base(userManager, accountEmailer, phoneService, roleRepository, identitySecurityLogManager, 
                accountProfilePictureContainer, settingManager, identityOptions, securityLogRepository, imageCompressor, 
                profilePictureOptions, applicationInfoAccessor, identityUserTwoFactorChecker, emailConfirmationCodeCache, 
                identityErrorDescriber, registerEmailConfirmationCodeOptions) 
        { 
            IdentityUserRepository = identityUserRepository; 
        } 
     
        public override async Task ResetPasswordAsync(ResetPasswordDto input) 
        { 
            await IdentityOptions.SetAsync(); 
            var user = await IdentityUserRepository.GetAsync(input.UserId, includeDetails: false); 
            (await UserManager.ResetPasswordAsync(user, input.ResetToken, input.Password)).CheckErrors(); 
     
            await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext 
            { 
                Identity = IdentitySecurityLogIdentityConsts.Identity, 
                Action = IdentitySecurityLogActionConsts.ChangePassword 
            }); 
        } 
    } 
     
    

    Thanks

    Hello,

    The ResetPasswordAsync method is called when POSTing the password. In my case, the issue I have can be reproduced only by loading the /Account/ResetPassword page which will only call the AccountAppService.VerifyPasswordResetTokenAsync. The AccountAppService.ResetPasswordAsync won't be called in that flow

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok, I will share a new version code snippet

    Thanks.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you try this?

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.Extensions.Options;
    using Volo.Abp;
    using Volo.Abp.Account;
    using Volo.Abp.Account.Emailing;
    using Volo.Abp.Account.PhoneNumber;
    using Volo.Abp.BlobStoring;
    using Volo.Abp.Caching;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.Identity;
    using Volo.Abp.Imaging;
    using Volo.Abp.SettingManagement;
    
    namespace MyCompanyName.MyProjectName;
    
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AccountAppService), typeof(IAccountAppService))]
    public class MyAccountAppService : AccountAppService
    {
        protected IIdentityUserRepository IdentityUserRepository { get; set; }
    
        public MyAccountAppService(
            IdentityUserManager userManager,
            IAccountEmailer accountEmailer,
            IAccountPhoneService phoneService,
            IIdentityRoleRepository roleRepository,
            IdentitySecurityLogManager identitySecurityLogManager,
            IBlobContainer<AccountProfilePictureContainer> accountProfilePictureContainer,
            ISettingManager settingManager,
            IOptions<IdentityOptions> identityOptions,
            IIdentitySecurityLogRepository securityLogRepository,
            IImageCompressor imageCompressor,
            IOptions<AbpProfilePictureOptions> profilePictureOptions,
            IApplicationInfoAccessor applicationInfoAccessor,
            IdentityUserTwoFactorChecker identityUserTwoFactorChecker,
            IDistributedCache<EmailConfirmationCodeCacheItem> emailConfirmationCodeCache,
            IdentityErrorDescriber identityErrorDescriber,
            IOptions<AbpRegisterEmailConfirmationCodeOptions> registerEmailConfirmationCodeOptions,
            IIdentityUserRepository identityUserRepository)
            : base(userManager, accountEmailer, phoneService, roleRepository, identitySecurityLogManager,
                accountProfilePictureContainer, settingManager, identityOptions, securityLogRepository, imageCompressor,
                profilePictureOptions, applicationInfoAccessor, identityUserTwoFactorChecker, emailConfirmationCodeCache,
                identityErrorDescriber, registerEmailConfirmationCodeOptions)
        {
            IdentityUserRepository = identityUserRepository;
        }
        
        
        public override async Task<bool> VerifyPasswordResetTokenAsync(VerifyPasswordResetTokenInput input)
        {
            var user = await IdentityUserRepository.GetAsync(input.UserId, includeDetails: false);
            return await UserManager.VerifyUserTokenAsync(
                user,
                UserManager.Options.Tokens.PasswordResetTokenProvider,
                UserManager<IdentityUser>.ResetPasswordTokenPurpose,
                input.ResetToken);
        }
    
        public override async Task ResetPasswordAsync(ResetPasswordDto input)
        {
            await IdentityOptions.SetAsync();
            var user = await IdentityUserRepository.GetAsync(input.UserId, includeDetails: false);
            (await UserManager.ResetPasswordAsync(user, input.ResetToken, input.Password)).CheckErrors();
    
            await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
            {
                Identity = IdentitySecurityLogIdentityConsts.Identity,
                Action = IdentitySecurityLogActionConsts.ChangePassword
            });
        }
    }
    
    
  • User Avatar
    0
    shodgson created

    Thanks for the snippet.

    The snippet does seem to help reduce the number of queries being made, but those queries still create sessions that remain idle in the connection pool until they’re removed 4–8 minutes later, as described here: https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling#remove-connections

    I reran my load test 3–4 times both with and without includeDetails: false, and the results were almost always the same. Without includeDetails: false, I consistently see about twice as many sessions being opened and then idled in the pool compared to when I use includeDetails: false.

    I also ran the load tests on other endpoints not directly related to Identity, and I observed the same behavior.

    Do you have any idea if there might be connections that aren’t being properly disposed somewhere?

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi

    I can confirm the problem is related to your database. It is not code problem.

    Can you test your app with local db?

    Thanks.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.1.0-preview. Updated on October 07, 2025, 05:59