-
Notifications
You must be signed in to change notification settings - Fork 404
[Prototype] Use IIdentityCache and CompositeCache #3439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ba03568
9af44ae
59d77da
144dfaf
1430801
f1a226a
8a3fd90
1357510
023bd24
81e5aae
939ca1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #if DO_NOT_COMPILE | ||
| using System; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Extensions.Caching.Memory; | ||
| using Microsoft.Identity.ServiceEssentials; | ||
|
|
||
| namespace Microsoft.Identity.Client.Cache.Prototype | ||
| { | ||
| internal class DefaultInMemoryCache : IIdentityCache | ||
| { | ||
| private readonly MemoryCache _memoryCache; | ||
|
|
||
| public DefaultInMemoryCache(CacheOptions cacheOptions) | ||
| { | ||
| _memoryCache = new MemoryCache(new MemoryCacheOptions() { SizeLimit = cacheOptions?.SizeLimit ?? 1000 }); | ||
| } | ||
|
|
||
| public Task<CacheEntry<T>> GetAsync<T>(string category, string key, CancellationToken cancellationToken = default) | ||
| where T : ICacheObject | ||
| { | ||
| CacheEntry<T> result = null; | ||
| _memoryCache?.TryGetValue(key, out result); | ||
| return Task.FromResult(result); | ||
| } | ||
|
|
||
| public Task<CacheEntry<T>> SetAsync<T>(string category, string key, T value, CacheEntryOptions cacheEntryOptions, CancellationToken cancellationToken = default) | ||
| where T : ICacheObject | ||
| { | ||
| var cacheEntry = new CacheEntry<T>(value, DateTimeOffset.UtcNow.Add(cacheEntryOptions.ExpirationTimeRelativeToNow), DateTimeOffset.UtcNow.Add(cacheEntryOptions.RefreshTimeRelativeToNow)); | ||
| var memoryCacheOptions = new MemoryCacheEntryOptions() | ||
| { | ||
| AbsoluteExpiration = cacheEntry.ExpirationTimeUTC, | ||
| Size = 1 | ||
| }; | ||
| return Task.FromResult(_memoryCache.Set(key, cacheEntry, memoryCacheOptions)); | ||
| } | ||
|
|
||
| #region Not Implemented | ||
| public Task RemoveAsync(string category, string key, CancellationToken cancellationToken = default) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
| public Task<CacheEntry<string>> GetAsync(string category, string key, CancellationToken cancellationToken = default) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| public Task<CacheEntry<string>> SetAsync(string category, string key, string value, CacheEntryOptions cacheEntryOptions, CancellationToken cancellationToken = default) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
| #endregion | ||
| } | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Identity.ServiceEssentials; | ||
| using Microsoft.Identity.ServiceEssentials.IdentityCache; | ||
| using Microsoft.IdentityModel.Abstractions; | ||
|
pmaytak marked this conversation as resolved.
|
||
|
|
||
| namespace Microsoft.Identity.Client.Cache.Prototype | ||
| { | ||
| internal class IdentityCacheWrapper | ||
| { | ||
| private static CacheOptions s_cacheOptions; | ||
| private readonly IIdentityCache _identityCache; | ||
| private static IIdentityLogger _identityLogger; | ||
| private static readonly Lazy<IIdentityCache> s_defaultIIdentityCache = new Lazy<IIdentityCache>( | ||
| () => CreateDefaultCache()); | ||
| private const string AppTokensCategory = "app_tokens"; | ||
| private const string UserTokensCategory = "user_tokens"; | ||
|
|
||
| // This cache instance (whether provided by the user or default one) will only ever be called/used if cache serialization is not enabled. | ||
| // There are three options for this cache: user-provided, static default, non-static default. | ||
| // User-provided cache takes precedence. | ||
| // Default cache is created lazily (since it's possible that token cache serialization is enabled) | ||
| internal IdentityCacheWrapper(CacheOptions cacheOptions, IIdentityLogger identityLogger) | ||
| { | ||
| s_cacheOptions = cacheOptions; | ||
| _identityLogger = identityLogger; | ||
| // Set (or overwrite) cache to user-specified implementation, otherwise set to default implementation, if not already set. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure about default implentation.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, definitely it's adding a dependency to .NET Memory Cache. Added it for simplicity to show that it could work E2E. Practically we could replace it with some other memory cache with eviction that doesn't have dependencies (eg MiseCache). |
||
|
|
||
| if (cacheOptions.IdentityCache != null) | ||
| { | ||
| _identityCache = cacheOptions?.IdentityCache; | ||
| } | ||
| else if (cacheOptions.UseSharedCache) | ||
| { | ||
| _identityCache = s_defaultIIdentityCache.Value; | ||
| } | ||
| else | ||
| { | ||
| _identityCache = CreateDefaultCache(); | ||
| } | ||
| } | ||
|
|
||
| private static IIdentityCache CreateDefaultCache() | ||
| { | ||
| var memoryCacheOptions = new InMemoryCacheOptions() | ||
| { | ||
| MaxNumberOfItemsForCategory = new Dictionary<string, int>() | ||
| { | ||
| { AppTokensCategory, s_cacheOptions.AppTokenCacheSizeLimit }, | ||
| { UserTokensCategory, s_cacheOptions.UserTokenCacheSizeLimit }, | ||
| } | ||
| }; | ||
|
|
||
| return new IdentityCachePrototype(memoryCacheOptions, _identityLogger, null); | ||
| } | ||
|
|
||
| internal async Task<T> GetAppCacheAsync<T>(string key) where T : ICacheObject, new() | ||
| { | ||
| return await GetAsync<T>(AppTokensCategory, key).ConfigureAwait(false); | ||
| } | ||
|
|
||
| internal async Task<T> GetUserCacheAsync<T>(string key) where T : ICacheObject, new() | ||
| { | ||
| return await GetAsync<T>(UserTokensCategory, key).ConfigureAwait(false); | ||
| } | ||
|
|
||
| private async Task<T> GetAsync<T>(string category, string key) where T : ICacheObject, new() | ||
| { | ||
| var entry = await _identityCache.GetAsync<T>(category, key).ConfigureAwait(false); | ||
| return entry == null ? default : entry.Value; | ||
| } | ||
|
|
||
| internal async Task SetAppCacheAsync<T>(string key, T value, DateTimeOffset? cacheExpiry) where T : ICacheObject, new() | ||
| { | ||
| await SetAsync<T>(AppTokensCategory, key, value, cacheExpiry).ConfigureAwait(false); | ||
| } | ||
|
|
||
| internal async Task SetUserCacheAsync<T>(string key, T value, DateTimeOffset? cacheExpiry) where T : ICacheObject, new() | ||
| { | ||
| await SetAsync<T>(UserTokensCategory, key, value, cacheExpiry).ConfigureAwait(false); | ||
| } | ||
|
|
||
| private async Task SetAsync<T>(string category, string key, T value, DateTimeOffset? cacheExpiry) where T : ICacheObject, new() | ||
| { | ||
| TimeSpan expirationTimeRelativeToNow = cacheExpiry.HasValue ? cacheExpiry.Value - DateTimeOffset.UtcNow : TimeSpan.FromHours(1); | ||
| await _identityCache.SetAsync(category, key, value, new CacheEntryOptions(expirationTimeRelativeToNow, 1)).ConfigureAwait(false); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.