Open Closed

Experience Table Locks on AbpUsers when using MicroService solution with Account.Pro, OpenIddict & ExternalLoginProvider #10657


User avatar
0
rhafer created

ABP Commercial (direct references) Volo.Abp.OpenIddict.Pro.* — 10.0.1 (e.g. EntityFrameworkCore, HttpApi, Application, Blazor., Domain.Shared, contracts). Volo.Abp.Account.Pro. — 10.0.1 (auth-server and Blazor packages such as Public.Web.OpenIddict, Admin., Public., Blazor server/WASM, HTTP API, etc.). Those versions are consistent across the projects that reference them (auth-server, identity service, administration service, Blazor).

Underlying OpenIddict (NuGet, transitive) After restore, apps/auth-server/PoweredByArray.AuthServer/obj/project.assets.json resolves the OpenIddict stack at 7.2.0, including packages such as OpenIddict.Core, OpenIddict.Abstractions, OpenIddict.Server, OpenIddict.Server.AspNetCore, OpenIddict.Validation, and related packages — all at 7.2.0.

So in short: ABP OpenIddict Pro / Account Pro = 10.0.1, and the OpenIddict libraries pulled in under that are 7.2.0.

  • Exception message and full stack trace:
    • Time out requests to requests that perform look-ups against the identity-service
  • Steps to reproduce the issue:
    • Enter a tenant
    • Sign in with an external provider
    • Prompts to register
    • Register the user. (POST /Account/Register?isExternalLogin=True)
    • Then the request times out & any other read operations on both (Identity and Auth Server) fail.

Restarting the (OpenIddict) Auth Server resolves the issue and we can continue normally. This is not sustainable in a production account.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

2 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    We reproduced this. The cause is in Volo.Abp.Account.Pro.Public.Web/Pages/Account/Register.cshtml.cs:

    OnPostAsync is wrapped in [UnitOfWork], and inside RegisterExternalUserAsync it awaits httpClient.GetStreamAsync(picture) to download the external provider's profile picture. The default HttpClient has no timeout, so when the picture URL is slow or unreachable (e.g. https://graph.microsoft.com/v1.0/me/photo/$value returning 404 or being blocked by your network), the transaction stays open with the AbpUsers X lock held, and every other read on that table is blocked. In the MicroService template the AuthServer also connects directly to the identity database, so the lock affects both services. Restarting AuthServer kills the connection, the transaction rolls back, and the lock is released — that's why a restart "fixes" it.

    Note: just lowering HttpClient.Timeout is not a full fix. The lock is still held until the HTTP call completes or times out, so the window only shrinks from ~100s to N seconds; concurrent reads are still blocked for N seconds on every external registration.

    Workaround

    Stop mapping the picture claim in your AuthServer module's ConfigureExternalProviders so the download path is skipped. Remove the picture-related ClaimActions for each external provider you use:

    .AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
    {
        // remove this line:
        // options.ClaimActions.MapCustomJson("picture", _ => "https://graph.microsoft.com/v1.0/me/photo/$value");
        options.SaveTokens = true;
    })
    .AddGoogle(GoogleDefaults.AuthenticationScheme, options =>
    {
        // remove this line:
        // options.ClaimActions.MapJsonKey(AbpClaimTypes.Picture, "picture");
    })
    .AddTwitter(TwitterDefaults.AuthenticationScheme, options =>
    {
        // remove this line:
        // options.ClaimActions.MapJsonKey(AbpClaimTypes.Picture, "profile_image_url_https");
        options.RetrieveUserDetails = true;
    });
    

    With the picture claim no longer set, RegisterExternalUserAsync skips the HTTP download entirely (the if (!picture.IsNullOrWhiteSpace()) branch is not entered), so the UoW transaction commits immediately after the INSERT AbpUsers and the lock is released. AuthServer no longer needs to be restarted.

    Trade-off: new external users won't have their avatar auto-imported from the provider; they can upload one later from the profile page.

    We'll address this properly in an upcoming release by moving the picture download outside the UoW transaction so the avatar import keeps working without holding the lock.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Quick update: the framework fix we'll ship is simpler than what I described earlier. Instead of moving the picture download out of the unit of work, we just bound the outbound HTTP call with a short timeout. The default HttpClient.Timeout is 100 seconds, which is why the lock could be held that long — capping it to 5 seconds is enough to make the AbpUsers row lock release on its own, so AuthServer no longer needs to be restarted.

    The patch adds a configurable option:

    public class AbpAccountOptions
    {
        // ...
        public TimeSpan ExternalProfilePictureDownloadTimeout { get; set; } = TimeSpan.FromSeconds(5);
    }
    

    And RegisterExternalUserAsync applies it to the HttpClient before GetStreamAsync. Profile pictures from external providers are normally tens of KB and finish in well under a second, so 5s leaves plenty of headroom for slow networks while preventing the lock from being held long enough to matter.

    If you want to apply the same fix locally before the next release, two options:

    1. Keep your current workaround (removing the picture ClaimActions mapping) — still valid and avoids the call entirely. Trade-off: no avatar auto-import.

    2. If you want to keep importing the avatar, override RegisterModel.RegisterExternalUserAsync in your own MyRegisterModel : RegisterModel and copy the original method, adding one line right after HttpClientFactory.CreateClient():

    var httpClient = HttpClientFactory.CreateClient();
    httpClient.Timeout = TimeSpan.FromSeconds(5); // add this line
    

    That alone makes the lock window predictable. With this in place, even when the external picture URL is slow or unreachable, the transaction commits within 5 seconds and the table is unlocked — no restart required.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
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.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.