-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #91 from mdsol/mauth_caching
[MCC-839304] Fix MAuth Caching issues
- Loading branch information
Showing
8 changed files
with
246 additions
and
85 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System; | ||
|
||
namespace Medidata.MAuth.Core.Caching | ||
{ | ||
/// <summary> | ||
/// Dto to store cache fields | ||
/// </summary> | ||
/// <typeparam name="TItem">Cache result type.</typeparam> | ||
public class CacheResult<TItem> | ||
{ | ||
/// <summary> | ||
/// Item to store in cache. | ||
/// </summary> | ||
public TItem Result { get; set; } | ||
|
||
/// <summary> | ||
/// Flag to determine if the result should be cache. | ||
/// </summary> | ||
public bool ShouldCache { get; set; } | ||
|
||
/// <summary> | ||
/// Cache duration. | ||
/// </summary> | ||
public TimeSpan AbsoluteCacheDuration { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Medidata.MAuth.Core.Caching | ||
{ | ||
/// <summary> | ||
/// Caching abstraction | ||
/// </summary> | ||
public interface ICacheService | ||
{ | ||
/// <summary> | ||
/// Gets or create cache item with locking. | ||
/// </summary> | ||
/// <typeparam name="TItem">The cached item type.</typeparam> | ||
/// <param name="key">Cache Key</param> | ||
/// <param name="factory">Factory method to create cached item</param> | ||
/// <returns>The item that was retrieved from the cache or created.</returns> | ||
Task<TItem> GetOrCreateWithLock<TItem>( | ||
string key, | ||
Func<Task<CacheResult<TItem>>> factory); | ||
|
||
/// <summary> | ||
/// Remove key from cache if exists. | ||
/// </summary> | ||
/// <param name="key"></param> | ||
void Remove(string key); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Caching.Memory; | ||
|
||
namespace Medidata.MAuth.Core.Caching | ||
{ | ||
/// <summary> | ||
/// In-Memory Cache service | ||
/// </summary> | ||
public class MemoryCacheService : ICacheService | ||
{ | ||
private readonly IMemoryCache _cache; | ||
|
||
/// <summary> | ||
/// In Memory Cache Service | ||
/// </summary> | ||
/// <param name="cache"></param> | ||
public MemoryCacheService(IMemoryCache cache) | ||
{ | ||
_cache = cache; | ||
} | ||
|
||
/// <summary> | ||
/// Checks if key is in the cache | ||
/// </summary> | ||
/// <param name="key"></param> | ||
/// <param name="value"></param> | ||
/// <typeparam name="T"></typeparam> | ||
/// <returns>True when key found in cache.</returns> | ||
public bool TryGetValue<T>(string key, out T value) | ||
=> _cache.TryGetValue(key, out value); | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="key"></param> | ||
public void Remove(string key) => _cache.Remove(key); | ||
|
||
/// <summary> | ||
/// Add an item to the cache. | ||
/// </summary> | ||
/// <param name="key">Cache Key</param> | ||
/// <param name="value">Item to cache</param> | ||
/// <param name="expiration">Cache Expiration</param> | ||
public void SetItem<T>(string key, T value, DateTimeOffset expiration) | ||
=> _cache.Set(key, value, expiration); | ||
|
||
/// <summary> | ||
/// Gets or create cache item with locking. | ||
/// </summary> | ||
/// <typeparam name="TItem">The cached item type.</typeparam> | ||
/// <param name="key">Cache Key</param> | ||
/// <param name="factory">Factory method to create cached item</param> | ||
/// <returns>Cached Item</returns> | ||
public async Task<TItem> GetOrCreateWithLock<TItem>( | ||
string key, | ||
Func<Task<CacheResult<TItem>>> factory) | ||
{ | ||
if (TryGetValue(key, out TItem item)) | ||
{ | ||
return item; | ||
} | ||
|
||
return await CreateAsyncLazyWithLock(key, factory); | ||
} | ||
|
||
[ExcludeFromCodeCoverage] | ||
private AsyncLazy<TItem> CreateAsyncLazyWithLock<TItem>(string key, Func<Task<CacheResult<TItem>>> factory) | ||
{ | ||
var asyncKey = CreateAsyncKey(key); | ||
|
||
if (TryGetValue(asyncKey, out AsyncLazy<TItem> asyncItem)) | ||
{ | ||
return asyncItem; | ||
} | ||
|
||
var lockStr = string.Intern("mutex_" + asyncKey); | ||
|
||
lock (lockStr) | ||
{ | ||
if (TryGetValue(asyncKey, out asyncItem)) | ||
{ | ||
return asyncItem; | ||
} | ||
|
||
if (TryGetValue(key, out TItem item)) | ||
{ | ||
return new AsyncLazy<TItem>(() => item); | ||
} | ||
|
||
asyncItem = new AsyncLazy<TItem>(() => CreateLazyFactory(key, factory)); | ||
|
||
_cache.Set(asyncKey, asyncItem); | ||
} | ||
|
||
return asyncItem; | ||
} | ||
|
||
private async Task<TItem> CreateLazyFactory<TItem>(string key, Func<Task<CacheResult<TItem>>> factory) | ||
{ | ||
try | ||
{ | ||
var result = await factory().ConfigureAwait(false); | ||
|
||
if (!result.ShouldCache) | ||
{ | ||
return result.Result; | ||
} | ||
|
||
using var entry = _cache.CreateEntry(key); | ||
entry.AbsoluteExpiration = DateTimeOffset.UtcNow + result.AbsoluteCacheDuration; | ||
entry.SetValue(result.Result); | ||
return result.Result; | ||
} | ||
finally | ||
{ | ||
var asyncKey = CreateAsyncKey(key); | ||
_cache.Remove(asyncKey); | ||
} | ||
|
||
} | ||
|
||
private string CreateAsyncKey(string key) => $"{key}-async"; | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Threading.Tasks; | ||
|
||
namespace Medidata.MAuth.Core.Caching | ||
{ | ||
/// <summary> | ||
/// Noop Cache Service | ||
/// </summary> | ||
[ExcludeFromCodeCoverage] | ||
public class NoopCacheService : ICacheService | ||
{ | ||
/// <summary> | ||
/// Gets or create cache item with locking. | ||
/// </summary> | ||
/// <typeparam name="TItem">The cached item type.</typeparam> | ||
/// <param name="key">Cache Key</param> | ||
/// <param name="factory">Factory method to create cached item</param> | ||
/// <returns>Cached Item</returns> | ||
public async Task<TItem> GetOrCreateWithLock<TItem>( | ||
string key, | ||
Func<Task<CacheResult<TItem>>> factory) | ||
{ | ||
var data = await factory().ConfigureAwait(false); | ||
return data.Result; | ||
} | ||
|
||
/// <summary> | ||
/// Remove key from cache if exists. | ||
/// </summary> | ||
/// <param name="key"></param> | ||
public void Remove(string key) | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.