Rozwiązywanie problemów z pamięcią podręczną tokenów w Microsoft.Identity.Web

Ten artykuł pomaga w diagnozowaniu i rozwiązywaniu problemów z pamięcią podręczną tokenów w Microsoft.Identity.Web. Problemy z pamięcią podręczną tokenu mogą powodować błędy uwierzytelniania, obniżoną wydajność lub nieoczekiwane monity logowania. Omówienie działania buforowania tokenów w Microsoft.Identity.Web, zobacz Omówienie pamięci podręcznej tokenów.

Wymagania wstępne

Przed rozpoczęciem rozwiązywania problemów potwierdź następujące kwestie:

  • Używasz obsługiwanej wersji Microsoft. Identity.Web.
  • Aplikacja ma buforowanie tokenów skonfigurowane w systemie Program.cs lub Startup.cs.
  • Masz dostęp do dzienników aplikacji i, jeśli ma to zastosowanie, infrastruktury rozproszonej pamięci podręcznej.

Włącz rejestrowanie i diagnostykę pamięci podręcznej tokenu

Włącz szczegółowe rejestrowanie jako pierwszy krok diagnostyczny. Microsoft.Identity.Web używa infrastruktury logowania ASP.NET Core i emituje zdarzenia za pośrednictwem biblioteki Microsoft Authentication Library (MSAL).

Włącz rejestrowanie MSAL

Ustaw poziom dziennikowania na Debug dla bibliotek tożsamości w pliku appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Identity.Web": "Debug",
      "Microsoft.IdentityModel": "Debug"
    }
  }
}

Subskrybuj zdarzenia bufora biblioteki MSAL

Subskrybuj zdarzenia powiadomień pamięci podręcznej tokenu BIBLIOTEKi MSAL, aby śledzić trafienia pamięci podręcznej, błędy i działanie serializacji:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.OnL2CacheFailure = (ex) =>
    {
        logger.LogWarning(ex, "L2 cache failure encountered.");
        // Return true to allow the operation to continue despite the cache failure.
        // Return false to propagate the exception.
        return true;
    };
});

Monitorowanie metryk pamięci podręcznej

W przypadku monitorowania produkcji śledź następujące kluczowe metryki:

  • Wskaźnik trafień pamięci podręcznej — niski wskaźnik trafień wskazuje, że tokeny nie są pobierane z pamięci podręcznej.
  • Opóźnienie pamięci podręcznej L2 — duże opóźnienie sugeruje problem z łącznością lub wydajnością rozproszonej pamięci podręcznej.
  • Błędy serializacji pamięci podręcznej — błędy podczas odczytu lub zapisu wskazują na uszkodzenie lub niezgodność wersji.
  • Zużycie pamięci — ciągły wzrost może wskazywać na brakujące polityki usuwania.

Błędy połączenia rozproszonej pamięci podręcznej (L2)

Objaw

Dzienniki aplikacji pokazują błędy przekroczenia limitu czasu połączenia lub sporadyczne błędy uwierzytelniania. Użytkownicy doświadczają opóźnień logowania i są widoczne wyjątki, takie jak:

Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache:
  StackExchange.Redis.RedisConnectionException: 
  No connection is active/available to service this operation.

Albo w przypadku rozproszonej pamięci podręcznej SQL Server:

Microsoft.Data.SqlClient.SqlException:
  A network-related or instance-specific error occurred while 
  establishing a connection to SQL Server.

Przyczyna

Magazyn kopii zapasowych rozproszonej pamięci podręcznej (Redis lub SQL Server) jest niemożliwy do osiągnięcia. Typowe przyczyny:

  • Nieprawidłowe parametry połączenia lub wygasłe poświadczenia dostępu.
  • Reguły zapory sieciowej blokujące połączenie z hosta aplikacji.
  • Usługa pamięci podręcznej nie działa lub przechodzi konserwację.
  • Niezgodność konfiguracji protokołu SSL/TLS między klientem a serwerem pamięci podręcznej.

Kroki diagnostyczne

Wykonaj następujące kroki, aby zidentyfikować błąd połączenia:

  1. Zweryfikuj łączność. Na hoście aplikacji przetestuj połączenie z usługą Redis lub SQL Server przy użyciu Test-NetConnection (PowerShell) lub redis-cli.
  2. Sprawdź ciąg połączenia. Upewnij się, że ciąg połączenia jest zgodny z nazwą hosta, portem i poświadczeniami serwera pamięci podręcznej.
  3. Przejrzyj reguły zapory. W Azure sprawdź, czy usługa App Service lub sieć wirtualna może uzyskać dostęp do zasobu pamięci podręcznej.
  4. Sprawdź kondycję usługi. W portalu Azure przejrzyj kondycję i metryki wystąpienia Azure Cache for Redis lub usługi SQL Database.

Rozwiązanie

Krok 1: Popraw łańcuch połączenia

Sprawdź ciąg połączenia w appsettings.json:

{
  "ConnectionStrings": {
    "Redis": "your-redis-instance.redis.cache.windows.net:6380,password=your-access-key,ssl=True,abortConnect=False"
  }
}

Ważna

Ustaw abortConnect=False w łańcuchu połączenia Redis. To ustawienie umożliwia aplikacji automatyczne ponowne nawiązywanie połączenia po przejściowych błędach połączenia, a nie natychmiastowe zgłaszanie.

Krok 2. Konfigurowanie ponawiania próby i odporności

Skonfiguruj wywołanie zwrotne OnL2CacheFailure, aby aplikacja mogła działać bez zakłóceń na wypadek tymczasowej niedostępności rozproszonej pamięci podręcznej.

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.OnL2CacheFailure = (ex) =>
    {
        // Log the failure for monitoring and alerting.
        logger.LogWarning(ex, "Distributed token cache is unavailable. " +
            "Falling back to in-memory cache.");
        return true; // Continue without the L2 cache.
    };

    // Set a timeout to avoid blocking the request pipeline.
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
});

Krok 3. Otwieranie reguł zapory

Jeśli aplikacja działa w Azure App Service, a pamięć podręczna znajduje się w sieci wirtualnej, dodaj wychodzące adresy IP usługi App Service do listy dozwolonych adresów zapory pamięci podręcznej.

Błędy deserializacji pamięci podręcznej

Objaw

Po uaktualnieniu Microsoft.Identity.Web lub MSAL.NET aplikacja zgłasza wyjątki deserializacji podczas odczytywania z rozproszonej pamięci podręcznej. Użytkownicy muszą zalogować się ponownie i zobaczysz wyjątki, takie jak:

System.Text.Json.JsonException:
  The JSON value could not be converted to the expected type.

Lub:

Microsoft.Identity.Client.MsalClientException:
  Error code: json_parse_failed

Przyczyna

Format serializacji pamięci podręcznej tokenu został zmieniony między wersjami biblioteki. Tokeny buforowane przez poprzednią wersję nie mogą być deserializowane przez nową wersję. Ten problem występuje najczęściej podczas uaktualnień wersji głównych MSAL.NET lub Microsoft. Identity.Web.

Rozwiązanie

Opcja A: Wyczyść pamięć podręczną

Najprostszą poprawką jest wyczyszczenie wszystkich wpisów w rozproszonej pamięci podręcznej. Użytkownicy ponownie uwierzytelniają się raz, a kolejne tokeny są zapisywane w nowym formacie.

Opróżnij pamięć podręczną Redis:

redis-cli FLUSHDB

Możesz też wyczyścić tabelę rozproszonej pamięci podręcznej SQL Server:

DELETE FROM [dbo].[TokenCache];

Uwaga / Notatka

Wyczyszczenie pamięci podręcznej powoduje ponowne uwierzytelnienie wszystkich aktywnych użytkowników. Zaplanuj tę operację podczas okna obsługi, jeśli aplikacja obsługuje dużą bazę użytkowników.

Opcja B: Płynna obsługa błędów deserializacji

Skonfiguruj adapter pamięci podręcznej, aby traktować błędy deserializacji jako chybienia pamięci podręcznej, a nie błędy krytyczne.

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.OnL2CacheFailure = (ex) =>
    {
        if (ex is JsonException or MsalClientException)
        {
            logger.LogWarning(ex, "Cache deserialization failed. " +
                "Treating as cache miss.");
            return true;
        }
        return false; // Propagate unexpected errors.
    };
});

Dzięki temu podejściu wpisy pamięci podręcznej, których dotyczy problem, są automatycznie zastępowane jako użytkownicy ponownie uwierzytelniają się i nie jest wymagane ręczne opróżnianie pamięci podręcznej.

Niezgodność klucza szyfrowania między serwerami

Objaw

Błędy deserializacji występują we wdrożeniach obejmujących wiele wystąpień, mimo że rozproszona pamięć podręczna działa. Tokeny buforowane przez jedno wystąpienie serwera nie mogą być odczytywane przez inne. W dziennikach są błędy json_parse_failed lub IDW10802 widoczne.

Przyczyna

Po włączeniu szyfrowania pamięci podręcznej (options.Encrypt = true) Microsoft.Identity.Web używa ASP.NET Core Data Protection do szyfrowania wpisów pamięci podręcznej. Domyślnie każde wystąpienie serwera generuje własne klucze ochrony danych, więc jedno wystąpienie nie może odszyfrować wpisów zapisanych przez inne.

Rozwiązanie

Skonfiguruj usługę ASP.NET Core Data Protection, aby udostępniać klucze szyfrowania we wszystkich wystąpieniach serwera.

Option A: Azure Blob Storage + Azure Key Vault (zalecane w przypadku wdrożeń Azure)

using Microsoft.AspNetCore.DataProtection;
using Azure.Identity;

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(
        new Uri("https://yourstorageaccount.blob.core.windows.net/dataprotection/keys.xml"),
        new DefaultAzureCredential())
    .ProtectKeysWithAzureKeyVault(
        new Uri("https://yourkeyvault.vault.azure.net/keys/dataprotection-key"),
        new DefaultAzureCredential());

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.Encrypt = true;
});

Ta konfiguracja przechowuje pierścień kluczy do ochrony danych w Azure Blob Storage i chroni klucze spoczywające za pomocą Azure Key Vault. Wszystkie instancje aplikacji, które uzyskują dostęp do wspólnego obiektu blob i klucza, mogą wzajemnie szyfrować i odszyfrowywać wpisy swoich pamięci podręcznych.

Opcja B: Udostępniony system plików z ochroną certyfikatów

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\keys"))
    .ProtectKeysWithCertificate(certificate);

Wskazówka

Podczas rotacji certyfikatu ochrony danych użyj polecenia UnprotectKeysWithAnyCertificate , aby uwzględnić zarówno bieżące, jak i poprzednie certyfikaty. Umożliwia to odszyfrowanie kluczy, które były chronione przy użyciu starego certyfikatu podczas okresu rotacji.

Zwiększenie pamięci przy użyciu pamięci podręcznej w pamięci

Objaw

Zużycie pamięci aplikacji stale rośnie wraz z upływem czasu. Jeśli aplikacja jest uruchamiana w kontenerze lub planie usługi App Service ze stałym limitem pamięci, z czasem może się uruchomić ponownie lub zgłosić błąd OutOfMemoryException. Monitorowanie pokazuje, że zarządzana sterta rośnie bez odzyskiwania pamięci przez odśmiecanie.

Przyczyna

Użycie AddInMemoryTokenCaches() bez limitów rozmiaru powoduje wzrost niezwiązanej pamięci podręcznej. Ta sytuacja jest szczególnie problematyczna w aplikacjach obsługujących wielu użytkowników, ponieważ wpis tokenu każdego użytkownika zużywa pamięć przez czas nieokreślony.

Domyślnie MemoryCache nie wymusza maksymalnego rozmiaru i nie wyklucza wpisów, chyba że ustawiono zasady wygasania.

Rozwiązanie

Opcja A: Ustaw limit rozmiaru i wygasanie przesuwne

Skonfiguruj pamięć podręczną w pamięci przy użyciu zasad wygasania:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

services.Configure<MsalMemoryTokenCacheOptions>(options =>
{
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
    options.SlidingExpiration = TimeSpan.FromHours(2);
});

Dzięki tym ustawieniom wpisy wygasają po 12 godzinach niezależnie od dostępu, a wpisy bezczynne przez 2 godziny są eksmitowane wcześniej.

Opcja B: Przełączanie do rozproszonej pamięci podręcznej

W przypadku aplikacji z wieloma współbieżnymi użytkownikami pamięć podręczna w pamięci nie jest skalowana. Przełącz się do rozproszonej pamięci podręcznej, takiej jak Redis:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
});

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

Rozproszona pamięć podręczna odciąża pamięć z procesu aplikacji, utrwala tokeny po ponownym uruchomieniu i obsługuje wdrożenia z wieloma wystąpieniami.

Opcja C: Użyj architektury hybrydowej L1/L2

Microsoft.Identity.Web obsługuje podejście hybrydowe, które łączy szybką pamięć podręczną L1 w pamięci z trwałą rozproszoną pamięcią podręczną L2. Skonfiguruj hybrydową pamięć podręczną L1/L2:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.L1CacheOptions = new MsalMemoryTokenCacheOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
        SlidingExpiration = TimeSpan.FromMinutes(2)
    };
});

W przypadku cache'owania L1/L2, często używane tokeny są obsługiwane z pamięci (L1) z opóźnieniem poniżej jednej milisekundy. Pamięć podręczna L2 zapewnia trwałość i spójność między wystąpieniami. Pamięć podręczna L1 używa krótkiego czasu wygaśnięcia w celu ograniczenia przyrostu pamięci.

Objaw

Użytkownicy są wielokrotnie monitowani o uwierzytelnianie wieloskładnikowe (MFA) lub zgodę, mimo że ostatnio wykonali te kroki. Aplikacja nie może odnaleźć istniejących tokenów w pamięci podręcznej.

Przyczyna

Ten problem występuje, gdy wyszukiwanie pamięci podręcznej tokenu nie pasuje do buforowanego wpisu na bieżącym koncie użytkownika. Typowe przyczyny:

  • Klucz pamięci podręcznej różni się od klucza użytego podczas przechowywania tokenu. Taka sytuacja może wystąpić, jeśli kontekst HomeAccountId lub dzierżawy ulegnie zmianie.
  • Aplikacja uruchamia wiele wystąpień za modułem równoważenia obciążenia z buforowaniem w pamięci, a żądania są kierowane do wystąpienia, które nie ma tokenu użytkownika.
  • Żądane oświadczenia lub zakresy uległy zmianie, więc buforowany token nie spełnia nowego wymagania.
  • Afinitet sesji nie jest włączony, dlatego użytkownicy są kierowani do różnych wystąpień, które nie mają zapisanych w pamięci podręcznej tokenów.

Kroki diagnostyczne

Wykonaj następujące kroki, aby określić, dlaczego tokeny nie znajdują się w pamięci podręcznej:

  1. Sprawdź typ pamięci podręcznej. Jeśli używasz AddInMemoryTokenCaches() w ramach wdrożenia z wieloma wystąpieniami, tokeny buforowane w jednym wystąpieniu nie są dostępne w innym. Przełącz się do rozproszonej pamięci podręcznej.
  2. Sprawdź identyfikator konta. Włącz rejestrowanie na poziomie debugowania i wyszukaj element HomeAccountId. Upewnij się, że identyfikator jest spójny we wszystkich żądaniach.
  3. Sprawdź zakresy. Upewnij się, że żądane zakresy przez GetAccessTokenForUserAsync odpowiadają zakresom, na które pierwotnie wyrażono zgodę. Niezgodność zakresu powoduje, że biblioteka Microsoft Authentication Library (MSAL) zażąda nowego tokena.
  4. Przejrzyj zasady dostępu warunkowego. Zasady dostępu warunkowego Microsoft Entra ID, które wymagają uwierzytelniania krokowego dla określonych zasobów, powodują dodatkowe monity niepowiązane z buforowaniem.

Rozwiązanie

Krok 1. Przełączanie do rozproszonego buforowania

Jeśli aplikacja uruchamia wiele wystąpień, użyj rozproszonej pamięci podręcznej, aby udostępniać tokeny między wystąpieniami:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
});

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

Krok 2. Weryfikowanie spójnych zakresów

Upewnij się, że zakresy żądane podczas uzyskiwania tokenów są zgodne z zakresami skonfigurowanymi podczas uwierzytelniania:

// In authentication setup — initial scopes.
.EnableTokenAcquisitionToCallDownstreamApi(new[] { "User.Read", "Mail.Read" })

// When acquiring a token — use the same scopes.
var token = await tokenAcquisition.GetAccessTokenForUserAsync(
    new[] { "User.Read", "Mail.Read" });

Krok 3. Włącz powiązanie sesji (tymczasowe obejście)

Jeśli nie możesz od razu przełączyć się do rozproszonej pamięci podręcznej, włącz koligację sesji (sesje przyklejone) w module równoważenia obciążenia. Koligacja sesji kieruje żądania użytkownika do tego samego wystąpienia. Takie podejście jest tymczasowym obejściem z ograniczeniami skalowalności.

Problemy z wydajnością pamięci podręcznej

Objaw

Pobieranie tokenu działa wolno, a wywołania interfejsu API podrzędnego zwiększają opóźnienie. Monitorowanie pokazuje wysoki średni czas odpowiedzi dla żądań pobierania tokenów. Opóźnienie nie pochodzi od dostawcy tożsamości — tokeny są serwowane z pamięci podręcznej.

Przyczyna

Problemy z wydajnością pamięci podręcznej zwykle wynikają z:

  • Wysokie opóźnienie pamięci podręcznej L2. Rozproszona pamięć podręczna jest silnie obciążona, geograficznie oddalona od aplikacji lub używa niedopasowanej warstwy usługi.
  • Duże wpisy pamięci podręcznej tokenu. Aplikacje buforujące tokeny dla wielu zasobów na użytkownika mogą tworzyć duże serializowane wpisy pamięci podręcznej, które są powolne do odczytu i zapisu.
  • Brak pamięci podręcznej L1. Każde pozyskanie tokenu przechodzi przez sieć do rozproszonej pamięci podręcznej, nawet w przypadku tokenów często używanych.

Rozwiązanie

Krok 1: Włącz pamięć podręczną L1

Pamięć podręczna L1 przechowuje często używane tokeny w pamięci procesu, unikając żądań sieciowych do L2.

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.L1CacheOptions = new MsalMemoryTokenCacheOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
        SlidingExpiration = TimeSpan.FromMinutes(2)
    };
});

W przypadku tej konfiguracji tokeny obsługiwane przez L1 mają opóźnienie podrzędne milisekund. Tokeny, które nie znajdują się w L1, są przenoszone do rozproszonej pamięci podręcznej L2.

Krok 2. Optymalizowanie warstwy rozproszonej pamięci podręcznej

Jeśli opóźnienie pamięci podręcznej L2 jest wysokie, rozważ następujące akcje:

  • Zwiększ rozmiar wystąpienia Redis. Przejdź do wyższej warstwy (na przykład z warstwy Podstawowa do warstwy Standardowa lub Premium w Azure Cache for Redis), aby uzyskać większą przepływność i mniejsze opóźnienia.
  • Włącz replikację geograficzną. Jeśli Twoja aplikacja obsługuje użytkowników w wielu regionach, użyj replikacji geograficznej Azure Cache for Redis, aby pamięć podręczna znajdowała się blisko zasobów obliczeniowych każdego regionu.
  • Przejrzyj konfigurację sieci. Użyj Private Link lub integracji z siecią wirtualną, aby zmniejszyć przeskoki sieciowe między aplikacją a pamięcią podręczną.

Krok 3. Zmniejszenie rozmiaru serializowanego tokenu

Jeśli wpisy pamięci podręcznej tokenu są duże, sprawdź, czy aplikacja żąda tokenów dla większej ilości zasobów niż jest to konieczne. Każda unikalna kombinacja zasobów i zakresu zwiększa rozmiar wpisu pamięci podręcznej. Skonsoliduj wywołania interfejsu API, jeśli to możliwe, aby zmniejszyć liczbę unikatowych tokenów dostępu buforowanych na użytkownika.

Usuwanie danych z pamięci podręcznej Redis

Objaw

Użytkownicy są sporadycznie monitowani o ponowne uwierzytelnienie bez wzorca opartego na wygaśnięciu tokenu. Monitorowanie usługi Redis pokazuje wzrost evicted_keys oraz zbliżanie się used_memory do limitu maxmemory.

Przyczyna

Gdy Redis osiąga swój limit maxmemory, usuwa klucze na podstawie skonfigurowanego limitu maxmemory-policy. Domyślne zasady (volatile-lru) usuwa najmniej niedawno używane klucze, których termin upłynął. Jeśli instancja Redis jest współdzielona z innymi danymi aplikacji, wpisy pamięci podręcznej tokenów konkurują o przestrzeń i mogą być przedwcześnie usuwane.

Rozwiązanie

Krok 1. Sprawdzanie zasad eksmisji

Sprawdź bieżące zasady eksmisji:

redis-cli CONFIG GET maxmemory-policy

W przypadku pamięci podręcznych tokenów volatile-lru (wartość domyślna) jest odpowiednia, ponieważ wpisy pamięci podręcznej tokenu mają terminy wygaśnięcia. Jeśli jednak inne dane bez daty wygaśnięcia zużywają pamięć, wpisy tokenu są najpierw usuwane.

Krok 2. Używanie dedykowanego wystąpienia usługi Redis

Izoluj pamięć podręczną tokenu od innych danych aplikacji, używając dedykowanej instancji Redis.

{
  "ConnectionStrings": {
    "RedisTokenCache": "token-cache-redis.redis.cache.windows.net:6380,password=...,ssl=True,abortConnect=False",
    "RedisAppData": "app-data-redis.redis.cache.windows.net:6380,password=...,ssl=True,abortConnect=False"
  }
}
// Register the token cache Redis instance specifically for distributed caching.
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("RedisTokenCache");
});

Krok 3. Zwiększenie limitu pamięci usługi Redis

Jeśli wystąpienie dedykowane nie jest możliwe, zwiększ ustawienie maxmemory. W Azure Cache for Redis przeprowadź skalowanie w górę do wyższej warstwy lub zwiększenie rozmiaru pamięci podręcznej.

Krok 4: Ustaw odpowiednie terminy wygaśnięcia wpisów pamięci podręcznej

Ustaw rozsądne wygasania, aby przestarzałe wpisy zostały usunięte przed upływem pamięci:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
    options.SlidingExpiration = TimeSpan.FromHours(2);
});

Wzrost tabeli rozproszonej pamięci podręcznej SQL

Objaw

Tabela rozproszonej pamięci podręcznej SQL stale rośnie, zużywając miejsce na dysku. Z upływem czasu zapytania bazy danych dotyczące tabeli pamięci podręcznej działają wolniej, i mogą pojawiać się ostrzeżenia dotyczące rozmiaru tabeli lub limitów magazynu.

Przyczyna

Rozproszona pamięć podręczna SQL Server (Microsoft.Extensions.Caching.SqlServer) nie powoduje automatycznego usunięcia wygasłych wpisów. Wygasłe wpisy pozostają do momentu jawnego przeczyszczenia, co powoduje nieograniczony wzrost tabeli, pogorszoną wydajność zapytań i zwiększone zużycie pamięci masowej.

Rozwiązanie

Krok 1. Konfigurowanie cyklicznego zadania oczyszczania

Utwórz zadanie agenta SQL Server lub zaplanowane zadanie do okresowego usuwania wygasłych wpisów.

-- Delete expired entries from the SQL distributed cache table.
-- Schedule this query to run every 30 minutes.
DELETE FROM [dbo].[TokenCache]
WHERE ExpiresAtTime < GETUTCDATE();

Wskazówka

W Azure SQL Database, gdzie SQL Server Agent nie jest dostępny, użyj Azure Automation, Azure Functions z wyzwalaczem zegarowym lub zadań elastycznych, aby zaplanować oczyszczanie.

Krok 2. Dodawanie indeksu w celu wydajnego czyszczenia

Jeśli tabela pamięci podręcznej nie ma jeszcze indeksu w kolumnie wygasania, dodaj jeden, aby przyspieszyć operację usuwania:

CREATE NONCLUSTERED INDEX IX_TokenCache_ExpiresAtTime
ON [dbo].[TokenCache] (ExpiresAtTime);

Krok 3. Monitorowanie rozmiaru tabeli

Dodaj monitorowanie, aby śledzić liczbę wierszy i rozmiar tabeli w czasie:

SELECT
    COUNT(*) AS TotalEntries,
    COUNT(CASE WHEN ExpiresAtTime < GETUTCDATE() THEN 1 END) AS ExpiredEntries,
    COUNT(CASE WHEN ExpiresAtTime >= GETUTCDATE() THEN 1 END) AS ActiveEntries
FROM [dbo].[TokenCache];

Krok 4. Rozważ przełączenie do usługi Redis

Jeśli zarządzanie czyszczeniem pamięci podręcznej SQL jest uciążliwe, przełącz się na Redis, który automatycznie przetwarza wygaśnięcie za pomocą wbudowanego mechanizmu TTL:

// Replace SQL distributed cache with Redis.
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
});

Ogólne wskazówki dotyczące rozwiązywania problemów

Skorzystaj z tych wskazówek, gdy problem nie pasuje do określonego scenariusza w tym artykule.

Sprawdzanie, czy pamięć podręczna jest używana

Dodaj tymczasowe rejestrowanie, aby potwierdzić, że tokeny są odczytywane i zapisywane w pamięci podręcznej:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.Encrypt = false; // Disable encryption temporarily for debugging only.
    options.OnL2CacheFailure = (ex) =>
    {
        logger.LogError(ex, "L2 cache operation failed.");
        return true;
    };
});

Sprawdź wiele rejestracji pamięci podręcznej

Jeśli w twoim kodzie startowym występuje wiele wywołań AddInMemoryTokenCaches() lub AddDistributedTokenCaches(), ostatnia rejestracja będzie obowiązywała. Sprawdź, czy zarejestrowano tylko jeden typ pamięci podręcznej.

Przeglądanie okresu istnienia tokenu

Tokeny dostępu mają skończony okres istnienia (zazwyczaj 60–90 minut). Jeśli użytkownicy zgłaszają ponowne uwierzytelnienie po tym okresie, jest to zachowanie oczekiwane, a nie problem z pamięcią podręczną. Tokeny odświeżania uzyskują nowe tokeny dostępu w trybie dyskretnym i są przechowywane w pamięci podręcznej. Jeśli brakuje lub wygasł token odświeżania, użytkownik musi ponownie uwierzytelnić.

Testowanie przy użyciu czystej pamięci podręcznej

Podczas diagnozowania problemów wyczyść pamięć podręczną, aby wykluczyć uszkodzone lub nieaktualne wpisy:

  • Pamięć podręczna w pamięci: Uruchom ponownie aplikację.
  • Redis: Uruchom proces FLUSHDB w bazie danych pamięci podręcznej.
  • SQL Server: Usuń wszystkie wiersze z tabeli pamięci podręcznej.

Pamięć podręczna tokenu jest pusta po ponownym uruchomieniu aplikacji

Objaw

Użytkownicy muszą ponownie uwierzytelniać się po ponownym uruchomieniu lub ponownym uruchomieniu aplikacji. Rozproszona pamięć podręczna jest wyświetlana jako pusta lub tokeny nie są utrwalane.

Przyczyna

Ten problem występuje zwykle w przypadku używania pamięci podręcznej w pamięci (AddInMemoryTokenCaches()) lub nietrwałej rozproszonej pamięci podręcznej (AddDistributedMemoryCache()) w środowisku produkcyjnym. Żadna z opcji nie utrzymuje tokenów między ponownymi uruchomieniami aplikacji.

AddDistributedMemoryCache() rejestruje implementację IDistributedCache , która przechowuje dane w pamięci. Pomimo nazwy "rozproszonej" dane nie są utrwalane zewnętrznie i są przeznaczone tylko do programowania i testowania.

Rozwiązanie

Przełącz się do trwałej rozproszonej pamięci podręcznej:

// Register a persistent cache (Redis example).
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "MyApp_";
});

// Use distributed token caches instead of in-memory.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

Ostrzeżenie

Nie należy mylić AddDistributedMemoryCache() z trwałą rozproszoną pamięcią podręczną. Użyj AddStackExchangeRedisCache() (Redis), AddDistributedSqlServerCache() (SQL Server) lub innej trwałej implementacji IDistributedCache dla obciążeń produkcyjnych.