L1 Cache in Distributed (L2) Token Cache - mattchenderson/microsoft-identity-web GitHub Wiki

More performant L1/L2 token cache in Microsoft Identity Web > 1.8.0

What?

Starting with Microsoft Identity Web 1.8.0, when connecting to a Distributed cache (L2=Level 2) cache, such as Redis, SQL or Cosmos db, Microsoft Identity Web will enable an InMemory (L1=Level 1) cache. This enables a more reliable and much more performant cache lookup, as the L2 cache, being distributed, is slower. Moreover, the L2 cache can fail, for example, due to a connectivity issue. The L1 cache will enable your customers to continue to sign-in and call protected web APIs.

Why?

Distributed token cache are less performant than memory, but they are more persistent. Also, when using a Distributed (L2) cache option, such as Redis or SQL, there can be issues with the L2 cache, such as the L2 cache is offline, and in versions of Microsoft Identity Web < 1.8.0 this would result in an app crash, unless handled by the developer.

How do I get started?

If you are using Microsoft Identity Web > 1.8.0, and a Distributed (L2) cache, Microsoft Identity Web will, by default, invoke the InMemory (L1) cache. As the developer, there is no work required on your end, this will happen automatically and you will benefit from reliability, increased performance and faster cache lookup with the L1 cache, while knowing the L2 cache is being populated.

Control the InMemory (L1) cache

Maybe you want to have fine grained control over the L1 cache? In the MsalDistributedTokenCacheAdapterOptions, you can set the L1CacheOptions which will be used by the distributed token cache adapter.

Controlling the size of the L1 cache is important. The L2 cache can grow a lot, but you probably want to control the impact of the L1 cache on the memory used by your app. We have defaulted the SizeLimit to 500 Mb, but you can set this value as you see fit for your app.

You would provide these options in Startup.cs:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
     options.L1CacheOptions.SizeLimit = 10 * 1024 * 1024; // 10 Mb
}

Handling an L2 cache Failure

When the L2 cache fails, Microsoft Identity Web will log an error, but proceed with the L1 cache. However, you might want to handle the error as soon as possible, so as to make sure persistence happens even if the app restarts. The error you'll see in the logs is similar to this one:

fail: Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapter[0]
      [MsIdWeb] DistributedCache: Connection issue. InRetry? False Error message: It was not possible to connect to the redis server(s). UnableToConnect on localhost:5002/Interactive, Initializing/NotStarted, last: NONE, origin: BeginConnectAsync, outstanding: 0, last-read: 2s ago, last-write: 2s ago, keep-alive: 60s, state: Connecting, mgr: 10 of 10 available, last-heartbeat: never, global: 9s ago, v: 2.2.4.27433

However, because of the L1 cache support, the end user will have no disruption to their sign-in experience, being able to sign-in and call a downstream web API. The L2 cache, when back online, will be eventually consistent with the L1 cache.

As part of the MsalDistributedTokenCacheAdapterOptions, you can also take advantage of the OnL2CacheFailure property, which you'll add to the Startup.cs and can add custom code for handling the above error by examining the exception. You can tell the distributed cache adapter to retry (return true), or not (return false).

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.L1CacheOptions.SizeLimit = 10 * 1024 * 1024; // 10 Mb
    options.OnL2CacheFailure = (ex) =>
    {
        if (ex is StackExchange.Redis.RedisConnectionException)
        {
            // Attempt to act on the redis cache if at all possible?
            // Put here your reconnected code
            return true; // Retry
        } 
        return false;  // Don't retry.
    };
});

Handling L2 cache eviction

See handle L2 cache eviction for details.