Skip to content

Commit

Permalink
Improve UseDbCallsIfCachingProviderIsDown, Close #216
Browse files Browse the repository at this point in the history
  • Loading branch information
VahidN committed Oct 31, 2023
1 parent 221b463 commit 429edc7
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 19 deletions.
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
services.AddEFSecondLevelCache(options =>
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))

// Please use the `CacheManager.Core` or `EasyCaching.Redis` for the Redis cache provider.
);
Expand Down Expand Up @@ -87,6 +89,8 @@ namespace EFSecondLevelCache.Core.AspNetCoreSample
const string providerName1 = "InMemory1";
services.AddEFSecondLevelCache(options =>
options.UseEasyCachingCoreProvider(providerName1, isHybridCache: false).DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);

// Add an in-memory cache service provider
Expand Down Expand Up @@ -144,6 +148,8 @@ namespace EFSecondLevelCache.Core.AspNetCoreSample
const string providerName1 = "Redis1";
services.AddEFSecondLevelCache(options =>
options.UseEasyCachingCoreProvider(providerName1, isHybridCache: false).DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);

// More info: https://easycaching.readthedocs.io/en/latest/Redis/
Expand Down Expand Up @@ -181,6 +187,9 @@ namespace EFSecondLevelCache.Core.AspNetCoreSample
}
```

[Here is a sample about it](/src/Tests/Issues/Issue123WithMessagePack/EFServiceProvider.cs).


### Using EasyCaching.Core as a dynamic cache provider

If you want to support multitenancy in your application and have a different Redis database per each tenant, first register multiple pre-configured providers with known `providerName`s and then select these `providerName`s based on the current tenant this way dynamically:
Expand All @@ -194,6 +203,8 @@ services.AddEFSecondLevelCache(options =>
.UseCacheKeyPrefix(serviceProvider => "EF_" + serviceProvider.GetRequiredService<IHttpContextAccesor>().HttpContext.Request.Headers["tenant-id"])
.DisableLogging(true)
.UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);
```

Expand Down Expand Up @@ -222,6 +233,8 @@ namespace EFSecondLevelCache.Core.AspNetCoreSample
{
services.AddEFSecondLevelCache(options =>
options.UseCacheManagerCoreProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);

// Add an in-memory cache service provider
Expand Down Expand Up @@ -272,6 +285,8 @@ services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));

services.AddEFSecondLevelCache(options =>
options.UseCacheManagerCoreProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);
```

Expand Down Expand Up @@ -346,7 +361,11 @@ NOTE: It doesn't matter where the `Cacheable` method is located in this expressi
Also it's possible to set the `Cacheable()` method's settings globally:

```csharp
services.AddEFSecondLevelCache(options => options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5)).DisableLogging(true).UseCacheKeyPrefix("EF_"));
services.AddEFSecondLevelCache(options => options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5)).DisableLogging(true)
.UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);
```

In this case the above query will become:
Expand Down Expand Up @@ -376,6 +395,8 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_");
options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));
// Fallback on db if the caching provider (redis) is down.
options.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1));
});

// ...
Expand All @@ -397,6 +418,8 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
/*.CacheQueriesContainingTypes(
CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableTypeComparison.Contains,
typeof(Post), typeof(Product), typeof(User)
Expand Down Expand Up @@ -426,6 +449,8 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
// How to skip caching specific commands
.SkipCachingCommands(commandText =>
commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase));
Expand All @@ -448,7 +473,7 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(true)
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
// Don't cache null values. Remove this optional setting if it's not necessary.
.SkipCachingResults(result =>
result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0));
Expand All @@ -470,6 +495,8 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
/*.CacheAllQueriesExceptContainingTypes(
CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),
typeof(Post), typeof(Product), typeof(User)
Expand Down Expand Up @@ -499,6 +526,8 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
.SkipCacheInvalidationCommands(commandText =>
// How to skip invalidating the related cache entries of this query
commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase));
Expand Down Expand Up @@ -535,6 +564,8 @@ First set the `DisableLogging(false)`:
```c#
services.AddEFSecondLevelCache(options =>
options.UseMemoryCacheProvider().DisableLogging(false).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider (redis) is down.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
```

And then change the log level to `Debug` in your `appsettings.json` file:
Expand Down
71 changes: 71 additions & 0 deletions src/EFCoreSecondLevelCacheInterceptor/EFCacheServiceCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;

namespace EFCoreSecondLevelCacheInterceptor;

/// <summary>
/// Is the configured cache provider online?
/// </summary>
public class EFCacheServiceCheck : IEFCacheServiceCheck
{
private readonly IEFCacheServiceProvider _cacheServiceProvider;
private readonly EFCoreSecondLevelCacheSettings _cacheSettings;

private bool? _isCacheServerAvailable;
private DateTime? _lastCheckTime;

/// <summary>
/// Is the configured cache provider online?
/// </summary>
public EFCacheServiceCheck(IOptions<EFCoreSecondLevelCacheSettings> cacheSettings,
IEFCacheServiceProvider cacheServiceProvider)
{
if (cacheSettings == null)
{
throw new ArgumentNullException(nameof(cacheSettings));
}

_cacheServiceProvider = cacheServiceProvider;
_cacheSettings = cacheSettings.Value;
}

/// <summary>
/// Is the configured cache services online and available? Can we use it without any problem?
/// </summary>
public bool IsCacheServiceAvailable()
{
if (!_cacheSettings.UseDbCallsIfCachingProviderIsDown)
{
return true;
}

var now = DateTime.UtcNow;

if (_lastCheckTime.HasValue &&
_isCacheServerAvailable.HasValue &&
now - _lastCheckTime.Value < _cacheSettings.NextCacheServerAvailabilityCheck)
{
return _isCacheServerAvailable.Value;
}

try
{
_lastCheckTime = now;
_ = _cacheServiceProvider.GetValue(new EFCacheKey(new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ "__Name__" }) { KeyHash = "__Test__" },
new EFCachePolicy());
_isCacheServerAvailable = true;
}
catch
{
_isCacheServerAvailable = false;
if (_cacheSettings.UseDbCallsIfCachingProviderIsDown)
{
throw;
}
}

return _isCacheServerAvailable.Value;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Entity Framework Core Second Level Caching Library.</Description>
<VersionPrefix>3.9.5</VersionPrefix>
<VersionPrefix>4.0.0</VersionPrefix>
<Authors>Vahid Nasiri</Authors>
<TargetFrameworks>net8.0;net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net462;netcoreapp3.1;</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,11 @@ public EFCoreSecondLevelCacheOptions DisableLogging(bool value = false)

/// <summary>
/// Fallback on db if the caching provider (redis) is down.
/// Its default value is false.
/// </summary>
public EFCoreSecondLevelCacheOptions UseDbCallsIfCachingProviderIsDown(bool value = false)
public EFCoreSecondLevelCacheOptions UseDbCallsIfCachingProviderIsDown(TimeSpan nextAvailabilityCheck)
{
Settings.UseDbCallsIfCachingProviderIsDown = value;
Settings.UseDbCallsIfCachingProviderIsDown = true;
Settings.NextCacheServerAvailabilityCheck = nextAvailabilityCheck;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public class EFCoreSecondLevelCacheSettings
/// </summary>
public bool UseDbCallsIfCachingProviderIsDown { set; get; }

/// <summary>
/// The cache server's availability check interval value.
/// </summary>
public TimeSpan NextCacheServerAvailabilityCheck { set; get; } = TimeSpan.FromMinutes(1);

/// <summary>
/// Possibility to allow caching with explicit transactions.
/// Its default value is false.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static IServiceCollection AddEFSecondLevelCache(

services.AddMemoryCache();
services.TryAddSingleton<IEFDebugLogger, EFDebugLogger>();
services.TryAddSingleton<IEFCacheServiceCheck, EFCacheServiceCheck>();
services.TryAddSingleton<IEFCacheKeyPrefixProvider, EFCacheKeyPrefixProvider>();
services.TryAddSingleton<IEFCacheKeyProvider, EFCacheKeyProvider>();
services.TryAddSingleton<IEFCachePolicyParser, EFCachePolicyParser>();
Expand Down
12 changes: 12 additions & 0 deletions src/EFCoreSecondLevelCacheInterceptor/IEFCacheServiceCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace EFCoreSecondLevelCacheInterceptor;

/// <summary>
/// Is the configured cache provider online?
/// </summary>
public interface IEFCacheServiceCheck
{
/// <summary>
/// Is the configured cache services online and available? Can we use it without any problem?
/// </summary>
bool IsCacheServiceAvailable();
}
Loading

0 comments on commit 429edc7

Please sign in to comment.