Skip to content

Commit

Permalink
Merge pull request #93 from mdsol/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
prajon84 authored Nov 1, 2021
2 parents 15cc4d6 + c6cdecc commit 459e55d
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 87 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changes in Medidata.MAuth

## v5.1.5
- **[Core]** Fix bug in MAuth caching response
## v5.1.4
- **[Core]** Change the fallback behavior of caching from 1 hour to 5 minutes in case when there is no valid caching
instruction provided by the MAuth server
Expand Down
49 changes: 0 additions & 49 deletions src/Medidata.MAuth.Core/CacheExtensions.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Medidata.MAuth.Core
namespace Medidata.MAuth.Core.Caching
{
[ExcludeFromCodeCoverage]
internal class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory)
Expand All @@ -17,8 +19,5 @@ public AsyncLazy(Func<Task<T>> taskFactory)
}

public TaskAwaiter<T> GetAwaiter() => Value.GetAwaiter();

public ConfiguredTaskAwaitable<T> ConfigureAwait(bool continueOnCapturedContext)
=> Value.ConfigureAwait(continueOnCapturedContext);
}
}
26 changes: 26 additions & 0 deletions src/Medidata.MAuth.Core/Caching/CacheResult.cs
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; }
}
}
28 changes: 28 additions & 0 deletions src/Medidata.MAuth.Core/Caching/ICacheService.cs
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);
}
}
127 changes: 127 additions & 0 deletions src/Medidata.MAuth.Core/Caching/MemoryCacheService.cs
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";

}
}
36 changes: 36 additions & 0 deletions src/Medidata.MAuth.Core/Caching/NoopCacheService.cs
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)
{
}
}
}
Loading

0 comments on commit 459e55d

Please sign in to comment.