Skip to content

Fallbacks, automatic/general invalidation, Redis Optimizations, IScopedCache #43

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
1ee1c80
wip
Dec 4, 2019
e938001
OnSyncProviderFailure
Dec 5, 2019
0bb4abc
cleanup
Dec 5, 2019
f800e40
restored TestHost.WebApi
Dec 5, 2019
e01aa97
new feature - GeneralInvalidationChannel
Dec 9, 2019
fd7622d
added unit tests and added disposal of new connection
Dec 9, 2019
44461ec
cleanup
Dec 9, 2019
f7cb22a
singleton redis connection per connection-name
Dec 11, 2019
3dac859
cleanup
Dec 11, 2019
7b9ce70
added automatic InvalidateOnUpdate to Notifier Policy options
Dec 12, 2019
0e5bc47
fixed InvalidateOnUpdate integration test
Dec 15, 2019
533c9cb
Refactoring and New Tests
Dec 18, 2019
b324aee
Merge branch 'feature/invalidations-and-fallbacks' into develop
Dec 18, 2019
89b3bb1
Notifier.InvalidOnUpdate -> LayeredCache.InvalidateLevel1OnLevel2Update
Jan 8, 2020
291866d
Added - InMemoryScopedCache
Jan 9, 2020
a8a268b
InMemoryScopedCache
Jan 11, 2020
5995963
Added - RedisScopedCache, LayeredScopedCache
Jan 12, 2020
a4850f9
added GetScopedCache
Jan 13, 2020
773118f
Added CacheableActionFilter for NETCore + safeguard
Jan 13, 2020
6890156
cleanups
Jan 13, 2020
400985e
fixed and aligned NETCore CacheableActionFilterAttribute
Jan 13, 2020
8a073fe
fixed valueTimestamp in LayeredScoped and on GetOrAdd scenarios
Jan 13, 2020
b17cad1
fixed MinimumValueTimestamp check
Jan 13, 2020
c3e4912
Bug fix (LayeredScopedCache) and cleanups
Jan 14, 2020
32cad71
Bug fix in InMemorySerializedCache.Set
Jan 14, 2020
fe95c50
some cleanups and added missing ConfigureAwait
Jan 14, 2020
ff9c9ac
wip
Jan 16, 2020
2bfa11b
Some Refactoring
Jan 21, 2020
b6560cb
ScopedContext is now internal class
Jan 22, 2020
55b52c8
bug fix and additional unit tests
Jan 22, 2020
b833c84
bug fix in RedisScopedCache
Jan 23, 2020
55d9873
Completed IScopedCache UnitTests Coverage
Jan 23, 2020
456fc1e
fixed Mocks.NoNotifier - NotImplementedExceptions
Jan 24, 2020
8bf3d7c
removed ConfigureAwait(false) from all unit tests
Jan 24, 2020
dd09ebc
bug fix - LayeredScopedCache validation
Jan 24, 2020
27117c1
Upgraded WebApiExtended.Swashbuckle csproj
Jan 27, 2020
eaed8d5
bug fixs and new features (see description)
Jan 30, 2020
5257836
2 bug fixes + new unit tests
Feb 2, 2020
bd0d1b2
wip
Feb 4, 2020
8a07a5f
Finishes: ICacheState, MongoDBCache, ReleaseNotes, ...
Feb 4, 2020
65864e8
Merge branch 'feature/fallbacks-and-automatic-invalidation' into develop
Feb 4, 2020
a0308be
bug fix, CacheDirectives context was not immutable
Feb 6, 2020
7b1af0f
Added load priority
Feb 9, 2020
5d7d24e
updated History.md
Feb 9, 2020
095effb
ConfigNode Name is now case insensitive
Feb 9, 2020
9969bb1
added IScopedCache implementation to NoCache
Feb 9, 2020
d488e50
cache/notifiers/connectionstrings are now case insensitive
Feb 10, 2020
d846743
InvalidateLevel1OnLevel2Upsert is now disabled for NoCache cache type
Feb 10, 2020
4c2da3c
cleanups and additional tests
Feb 10, 2020
1aeac81
New CacheExtensions, LayeredCache more resilient (ClearCache, isactive)
Feb 11, 2020
a133259
PR Amit
Feb 13, 2020
9fd1969
cleanup (duplicated endpoint)
Feb 13, 2020
5abd71d
added InvalidateLevel1OnLevel2Upsert to LayeredScopedCache
Mar 3, 2020
ba2ade4
added DefaultCacheDirectives to CacheableActionFilterAttribute
Mar 3, 2020
ac20734
Merge branch 'master' into feature/fallbacks-and-automatic-invalidation
ybqwerty May 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AopCaching.UnitTests/Mocks/MockCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ protected override bool TryGetInner<TValue>(string key, out TValue value)
{
hits++;
// ReSharper disable once CanBeReplacedWithTryCastAndCheckForNull
value = item.Value is TValue ? (TValue)item.Value : default(TValue);
value = item.Value is TValue ? (TValue)item.Value : default;
return true;
}

misses++;
value = default(TValue);
value = default;
return false;
}

Expand Down
54 changes: 26 additions & 28 deletions AopCaching/AopCaching.csproj
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>PubComp.Caching.AopCaching</AssemblyName>
<RootNamespace>PubComp.Caching.AopCaching</RootNamespace>
<Version>4.4.2</Version>
<!--Only change AssemblyVersion for major versions!--><AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.4.2.0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PostSharp" Version="6.0.28" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="readme.txt" pack="true" PackagePath="." />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="&quot;$(SolutionDir).NuGetPack\NuGetPack.exe&quot; &quot;$(ProjectPath)&quot; &quot;$(TargetPath)&quot; $(ConfigurationName)" />
</Target>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>PubComp.Caching.AopCaching</AssemblyName>
<RootNamespace>PubComp.Caching.AopCaching</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>5.0.0</Version>
<AssemblyVersion>5.0.0.0</AssemblyVersion>
<FileVersion>5.0.0.0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PostSharp" Version="6.0.28" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="&quot;$(SolutionDir).NuGetPack\NuGetPack.exe&quot; &quot;$(ProjectPath)&quot; &quot;$(TargetPath)&quot; $(ConfigurationName)" />
</Target>
</Project>
39 changes: 21 additions & 18 deletions AopCaching/CacheAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Linq;
using System.Threading;
using PostSharp.Aspects;
using PostSharp.Aspects;
using PubComp.Caching.Core;
using PubComp.Caching.Core.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using PubComp.Caching.Core.Attributes;

namespace PubComp.Caching.AopCaching
{
Expand Down Expand Up @@ -71,8 +71,7 @@ public sealed override void OnInvoke(MethodInterceptionArgs args)
}

var cacheToUse = this.cache;

if (cacheToUse == null)
if (!cacheToUse.IsUseable())
{
base.OnInvoke(args);
return;
Expand All @@ -94,18 +93,22 @@ public sealed override async Task OnInvokeAsync(MethodInterceptionArgs args)
}

var cacheToUse = this.cache;

if (cacheToUse == null)
if (!cacheToUse.IsUseable())
{
await base.OnInvokeAsync(args);
}
else
{
var key = GetCacheKey(args);
var result = await cacheToUse.GetAsync(key, async () => { await base.OnInvokeAsync(args); return args.ReturnValue; });
var returnType = GetReturnType(args.Method);
args.ReturnValue = SafeCasting.CastTo(returnType, result);
await base.OnInvokeAsync(args).ConfigureAwait(false);
return;
}

var key = GetCacheKey(args);
var result = await cacheToUse
.GetAsync(key, async () =>
{
await base.OnInvokeAsync(args).ConfigureAwait(false);
return args.ReturnValue;
})
.ConfigureAwait(false);
var returnType = GetReturnType(args.Method);
args.ReturnValue = SafeCasting.CastTo(returnType, result);
}

private string GetCacheKey(MethodInterceptionArgs args)
Expand All @@ -125,7 +128,7 @@ private string GetCacheKey(MethodInterceptionArgs args)
var key = new CacheKey(classNameNonGeneric, this.methodName, parameterTypeNamesNonGeneric, parameterValues, genericArgumentTypeNames).ToString();
return key;
}

private static Type GetReturnType(MethodBase method)
{
var returnType = (method as MethodInfo)?.ReturnType;
Expand Down
48 changes: 35 additions & 13 deletions AopCaching/CacheListAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,7 @@ public sealed override void OnInvoke(MethodInterceptionArgs args)
}

var cacheToUse = this.cache;

if (cacheToUse == null)
if (!cacheToUse.IsUseable())
{
base.OnInvoke(args);
return;
Expand Down Expand Up @@ -252,16 +251,28 @@ public sealed override void OnInvoke(MethodInterceptionArgs args)
return;
}

var newValuesTimestamp = DateTimeOffset.UtcNow;

args.Arguments[this.keyParameterNumber] = missingKeys;
base.OnInvoke(args);
var resultsFromerInner = args.ReturnValue;
addDataRange.Invoke(resultList, new [] { resultsFromerInner });
var resultsFromInner = args.ReturnValue;
addDataRange.Invoke(resultList, new [] { resultsFromInner });

var values = GetKeyValues(args, resultsFromerInner, parameterValues);
var values = GetKeyValues(args, resultsFromInner, parameterValues);

foreach (var value in values)
if (cacheToUse is IScopedCache scopedCacheToUse)
{
cacheToUse.Set(value.Key, value.Value);
foreach (var value in values)
{
scopedCacheToUse.SetScoped(value.Key, value.Value, newValuesTimestamp);
}
}
else
{
foreach (var value in values)
{
cacheToUse.Set(value.Key, value.Value);
}
}

args.ReturnValue = resultList;
Expand All @@ -276,8 +287,7 @@ public sealed override async Task OnInvokeAsync(MethodInterceptionArgs args)
}

var cacheToUse = this.cache;

if (cacheToUse == null)
if (!cacheToUse.IsUseable())
{
await base.OnInvokeAsync(args).ConfigureAwait(false);
return;
Expand Down Expand Up @@ -315,20 +325,32 @@ public sealed override async Task OnInvokeAsync(MethodInterceptionArgs args)
return;
}

var newValuesTimestamp = DateTimeOffset.UtcNow;

args.Arguments[this.keyParameterNumber] = missingKeys;
await base.OnInvokeAsync(args).ConfigureAwait(false);
var resultsFromerInner = args.ReturnValue;
addDataRange.Invoke(resultList, new [] { resultsFromerInner });

var values = GetKeyValues(args, resultsFromerInner, parameterValues);

var tasks = values.Select(async x => await cacheToUse.SetAsync(x.Key, x.Value).ConfigureAwait(false));
await Task.WhenAll(tasks).ConfigureAwait(false);
if (cacheToUse is IScopedCache scopedCacheToUse)
{
var tasks = values.Select(async x => await scopedCacheToUse
.SetScopedAsync(x.Key, x.Value, newValuesTimestamp).ConfigureAwait(false));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
else
{
var tasks = values.Select(async x => await cacheToUse
.SetAsync(x.Key, x.Value).ConfigureAwait(false));
await Task.WhenAll(tasks).ConfigureAwait(false);
}

args.ReturnValue = resultList;
}

private Dictionary<string, object> GetKeyValues(MethodInterceptionArgs args, object resultsFromerInner, object[] parameterValues)
private Dictionary<string, object> GetKeyValues(MethodInterceptionArgs args, object resultsFromInner, object[] parameterValues)
{
var converter = createDataKeyConverter.Invoke(new object[0]);

Expand All @@ -341,7 +363,7 @@ private Dictionary<string, object> GetKeyValues(MethodInterceptionArgs args, obj
: args.Method.GetParameters().Select(p => p.ParameterType.FullName).ToArray();

Dictionary<string, object> values = new Dictionary<string, object>();
foreach (object result in resultsFromerInner as IEnumerable)
foreach (object result in resultsFromInner as IEnumerable)
{
var k = convertDataToKey.Invoke(converter, new[] {result});

Expand Down
16 changes: 14 additions & 2 deletions Caching.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{57585B9A-7003-461A-B812-CA48603C839F}"
EndProject
Expand Down Expand Up @@ -49,6 +49,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestHost.WebApi", "WebApi\T
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiExtended.NETCore", "WebApiExtended.NETCore\WebApiExtended.NETCore.csproj", "{FAF640A8-7284-4587-8908-560E3BBFB56F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiExtended.Swashbuckle", "WebApiExtended.Swashbuckle\WebApiExtended.Swashbuckle.csproj", "{C0F9B07A-F480-424F-AF19-DCA2FA288318}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiExtended.NETCore.Swashbuckle", "WebApiExtended.NETCore.Swashbuckle\WebApiExtended.NETCore.Swashbuckle.csproj", "{7F3FDEDB-48D7-4BB0-96BD-49107B6A6EC0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -111,6 +115,14 @@ Global
{FAF640A8-7284-4587-8908-560E3BBFB56F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAF640A8-7284-4587-8908-560E3BBFB56F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAF640A8-7284-4587-8908-560E3BBFB56F}.Release|Any CPU.Build.0 = Release|Any CPU
{C0F9B07A-F480-424F-AF19-DCA2FA288318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0F9B07A-F480-424F-AF19-DCA2FA288318}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0F9B07A-F480-424F-AF19-DCA2FA288318}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0F9B07A-F480-424F-AF19-DCA2FA288318}.Release|Any CPU.Build.0 = Release|Any CPU
{7F3FDEDB-48D7-4BB0-96BD-49107B6A6EC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F3FDEDB-48D7-4BB0-96BD-49107B6A6EC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F3FDEDB-48D7-4BB0-96BD-49107B6A6EC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F3FDEDB-48D7-4BB0-96BD-49107B6A6EC0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
48 changes: 48 additions & 0 deletions Core.UnitTests/CacheControllerUtilTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class CacheControllerUtilTests
private Mocks.MockMemCache cache1;
private Mocks.MockMemCache cache2;
private Mocks.MockMemCache cache3;
private Mocks.MockMemCache cache4;

[TestInitialize]
public void TestInitialize()
Expand All @@ -32,6 +33,9 @@ public void TestInitialize()
cache3 = new Mocks.MockMemCache(typeof(TestProvider).FullName);
CacheManager.SetCache(cache3.Name, cache3);

cache4 = new Mocks.MockMemCache("mockCache*");
CacheManager.SetCache(cache4.Name, cache4);

this.cacheControllerUtil = new CacheControllerUtilExposed();
this.cacheControllerUtil.ClearRegistrations();

Expand Down Expand Up @@ -339,6 +343,34 @@ public void TestCacheRefreshItem_EmptyKey()
this.cacheControllerUtil.RefreshCacheItem(string.Empty, null);
}

[TestMethod]
public void TestCacheRefreshItemAopImpliedNamed()
{
this.cacheControllerUtil.RegisterCacheItem(
() => new TestProvider().GetData4("v1", 2, true), true);

Assert.AreEqual(0, cache4.Hits);
Assert.AreEqual(1, TestProvider.Hits4);

var result = new TestProvider().GetData4("v1", 2, true);
Assert.AreEqual("GetData-v1/2/True-1", result);
Assert.AreEqual(1, TestProvider.Hits4);
Assert.AreEqual(1, cache4.Hits);
}

[TestMethod]
public void TestCacheRefreshItemAopImpliedNamedNoCache()
{
this.cacheControllerUtil.RegisterCacheItem(
() => new TestProvider().GetData0("v1", 2, true), true);

Assert.AreEqual(1, TestProvider.Hits0);

var result = new TestProvider().GetData0("v1", 2, true);
Assert.AreEqual("GetData-v1/2/True-2", result);
Assert.AreEqual(2, TestProvider.Hits0);
}

[TestMethod]
public void TestCacheRefreshItemAopNamed()
{
Expand Down Expand Up @@ -409,8 +441,10 @@ public class SubClass1 : BaseClass
public class TestProvider
{
// ReSharper disable RedundantDefaultMemberInitializer
internal static int Hits0 = 0;
internal static int Hits1 = 0;
internal static int Hits2 = 0;
internal static int Hits4 = 0;

[Cache("cache1")]
public string GetData(string v1, int v2, bool v3)
Expand All @@ -425,6 +459,20 @@ public string GetData2(string v1, int v2, bool v3)
var hits = Interlocked.Increment(ref Hits2);
return string.Concat("GetData2-", v1, '/', v2, '/', v3, '-', hits);
}

[Cache("cache0")]
public string GetData0(string v1, int v2, bool v3)
{
var hits = Interlocked.Increment(ref Hits0);
return string.Concat("GetData-", v1, '/', v2, '/', v3, '-', hits);
}

[Cache("mockCache0")]
public string GetData4(string v1, int v2, bool v3)
{
var hits = Interlocked.Increment(ref Hits4);
return string.Concat("GetData-", v1, '/', v2, '/', v3, '-', hits);
}
}

#endregion
Expand Down
Loading