Skip to content

Commit

Permalink
provide option to disable L1 cache (#1392)
Browse files Browse the repository at this point in the history
* provide option to disable L1 cache

* fix warning
  • Loading branch information
jennyf19 authored Aug 17, 2021
1 parent 1f46bd9 commit 26f1a15
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public partial class MsalDistributedTokenCacheAdapter : MsalAbstractTokenCachePr
/// .NET Core Memory cache.
/// </summary>
internal /*for tests*/ readonly IDistributedCache _distributedCache;
internal /*for tests*/ readonly MemoryCache _memoryCache;
internal /*for tests*/ readonly MemoryCache? _memoryCache;
private readonly ILogger<MsalDistributedTokenCacheAdapter> _logger;
private readonly TimeSpan? _expirationTime;
private readonly string _distributedCacheType = "DistributedCache"; // for logging
Expand Down Expand Up @@ -57,7 +57,12 @@ public MsalDistributedTokenCacheAdapter(

_distributedCache = distributedCache;
_distributedCacheOptions = distributedCacheOptions.Value;
_memoryCache = new MemoryCache(_distributedCacheOptions.L1CacheOptions ?? new MemoryCacheOptions { SizeLimit = 500 * 1024 * 1024 });

if (!_distributedCacheOptions.DisableL1Cache)
{
_memoryCache = new MemoryCache(_distributedCacheOptions.L1CacheOptions ?? new MemoryCacheOptions { SizeLimit = 500 * 1024 * 1024 });
}

_logger = logger;

if (_distributedCacheOptions.AbsoluteExpirationRelativeToNow != null)
Expand Down Expand Up @@ -110,9 +115,13 @@ protected override async Task RemoveKeyAsync(string cacheKey)
protected override async Task RemoveKeyAsync(string cacheKey, CacheSerializerHints cacheSerializerHints)
{
string remove = "Remove";
_memoryCache.Remove(cacheKey);

Logger.MemoryCacheRemove(_logger, _memoryCacheType, remove, cacheKey, null);
if (_memoryCache != null)
{
_memoryCache.Remove(cacheKey);

Logger.MemoryCacheRemove(_logger, _memoryCacheType, remove, cacheKey, null);
}

await L2OperationWithRetryOnFailureAsync(
remove,
Expand Down Expand Up @@ -143,9 +152,14 @@ protected override async Task<byte[]> ReadCacheBytesAsync(string cacheKey)
protected override async Task<byte[]> ReadCacheBytesAsync(string cacheKey, CacheSerializerHints cacheSerializerHints)
{
string read = "Read";
// check memory cache first
byte[]? result = (byte[])_memoryCache.Get(cacheKey);
Logger.MemoryCacheRead(_logger, _memoryCacheType, read, cacheKey, result?.Length ?? 0, null);
byte[]? result = null;

if (_memoryCache != null)
{
// check memory cache first
result = (byte[])_memoryCache.Get(cacheKey);
Logger.MemoryCacheRead(_logger, _memoryCacheType, read, cacheKey, result?.Length ?? 0, null);
}

if (result == null)
{
Expand All @@ -163,18 +177,21 @@ protected override async Task<byte[]> ReadCacheBytesAsync(string cacheKey, Cache

Logger.DistributedCacheReadTime(_logger, _distributedCacheType, read, measure.MilliSeconds, null);

// back propagate to memory cache
if (result != null)
if (_memoryCache != null)
{
MemoryCacheEntryOptions memoryCacheEntryOptions = new MemoryCacheEntryOptions()
// back propagate to memory cache
if (result != null)
{
AbsoluteExpirationRelativeToNow = _expirationTime,
Size = result?.Length,
};

Logger.BackPropagateL2toL1(_logger, memoryCacheEntryOptions.Size ?? 0, null);
_memoryCache.Set(cacheKey, result, memoryCacheEntryOptions);
Logger.MemoryCacheCount(_logger, _memoryCacheType, read, _memoryCache.Count, null);
MemoryCacheEntryOptions memoryCacheEntryOptions = new MemoryCacheEntryOptions()
{
AbsoluteExpirationRelativeToNow = _expirationTime,
Size = result?.Length,
};

Logger.BackPropagateL2toL1(_logger, memoryCacheEntryOptions.Size ?? 0, null);
_memoryCache.Set(cacheKey, result, memoryCacheEntryOptions);
Logger.MemoryCacheCount(_logger, _memoryCacheType, read, _memoryCache.Count, null);
}
}
}
else
Expand Down Expand Up @@ -222,25 +239,28 @@ protected override async Task WriteCacheBytesAsync(
cacheExpiry = cacheSerializerHints.SuggestedCacheExpiry.Value.UtcDateTime - DateTime.UtcNow;
}

MemoryCacheEntryOptions memoryCacheEntryOptions = new MemoryCacheEntryOptions()
if (_memoryCache != null)
{
AbsoluteExpirationRelativeToNow = cacheExpiry ?? _expirationTime,
Size = bytes?.Length,
};

// write in both
_memoryCache.Set(cacheKey, bytes, memoryCacheEntryOptions);
Logger.MemoryCacheRead(_logger, _memoryCacheType, write, cacheKey, bytes?.Length ?? 0, null);
Logger.MemoryCacheCount(_logger, _memoryCacheType, write, _memoryCache.Count, null);
MemoryCacheEntryOptions memoryCacheEntryOptions = new MemoryCacheEntryOptions()
{
AbsoluteExpirationRelativeToNow = cacheExpiry ?? _expirationTime,
Size = bytes?.Length,
};

// write in both
_memoryCache.Set(cacheKey, bytes, memoryCacheEntryOptions);
Logger.MemoryCacheRead(_logger, _memoryCacheType, write, cacheKey, bytes?.Length ?? 0, null);
Logger.MemoryCacheCount(_logger, _memoryCacheType, write, _memoryCache.Count, null);
}

await L2OperationWithRetryOnFailureAsync(
write,
(cacheKey) => _distributedCache.SetAsync(
cacheKey,
bytes,
_distributedCacheOptions,
cacheSerializerHints?.CancellationToken ?? CancellationToken.None),
cacheKey).Measure().ConfigureAwait(false);
write,
(cacheKey) => _distributedCache.SetAsync(
cacheKey,
bytes,
_distributedCacheOptions,
cacheSerializerHints?.CancellationToken ?? CancellationToken.None),
cacheKey).Measure().ConfigureAwait(false);
}

private async Task L2OperationWithRetryOnFailureAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,13 @@ public class MsalDistributedTokenCacheAdapterOptions : DistributedCacheEntryOpti
/// </summary>
/// The default is <c>false.</c>
public bool Encrypt { get; set; }

/// <summary>
/// Disable the L1 (InMemory) cache.
/// Useful in scenarios where multiple apps share the same
/// L2 cache.
/// </summary>
/// The default is <c>false.</c>
public bool DisableL1Cache { get; set; }
}
}
34 changes: 34 additions & 0 deletions tests/Microsoft.Identity.Web.Test/L1L2CacheOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,40 @@ public void MsalDistributedTokenCacheAdapterOptions_L1ExpirationTimeRatio(double
Assert.Equal(0, testCache._memoryCache.Count);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void MsalDistributedTokenCacheAdapterOptions_DisableL1Cache(bool disableL1Cache)
{
// Arrange
var msalDistributedTokenOptions = Options.Create(
new MsalDistributedTokenCacheAdapterOptions()
{
DisableL1Cache = disableL1Cache,
});
BuildTheRequiredServices();

// Act
var testCache = new TestMsalDistributedTokenCacheAdapter(
new TestDistributedCache(),
msalDistributedTokenOptions,
_provider.GetService<ILogger<MsalDistributedTokenCacheAdapter>>());

// Assert
Assert.NotNull(testCache);
Assert.NotNull(testCache._distributedCache);

if (!disableL1Cache)
{
Assert.NotNull(testCache._memoryCache);
Assert.Equal(0, testCache._memoryCache.Count);
}
else
{
Assert.Null(testCache._memoryCache);
}
}

private void BuildTheRequiredServices()
{
var services = new ServiceCollection();
Expand Down
1 change: 1 addition & 0 deletions tests/WebAppCallsWebApiCallsGraph/Client/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public void ConfigureServices(IServiceCollection services)
});
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
//options.DisableL1Cache = true;
options.OnL2CacheFailure = (ex) =>
{
if (ex is StackExchange.Redis.RedisConnectionException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void ConfigureServices(IServiceCollection services)
});
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
//options.DisableL1Cache = true;
options.OnL2CacheFailure = (ex) =>
{
if (ex is StackExchange.Redis.RedisConnectionException)
Expand Down

0 comments on commit 26f1a15

Please sign in to comment.