-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Support Distributed Caching bypass capability #10297
Conversation
Encapsulates distributed caching and adds cache bypass when Redis has an issue
|
I like the idea of making it redundant to make sure it will work properly. Though, I think the implementation you did forces us to use a Redis Cache while the IDistributedCache should be abstract. |
|
||
namespace OrchardCore.Caching.Distributed | ||
{ | ||
public interface IDistributedCache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the problem is that if you use the same interface and AddStackExchangeRedis() you either have to extract/wrap the dependency it creates OR stop using that call and recreate it.
I actually thought of a solution last night.. in the constructor for DocumentManager it could ask for a MS IDistributedCache and then wrap it as long as I changed the RedisDistributedCache into a more generic version.
using Microsoft.Extensions.Caching.Distributed; | ||
using OrchardCore.Modules; | ||
|
||
namespace OrchardCore.Redis.Caching |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move it to the Services
folder where we already have RedisBus
and RedisLock
.
public RedisDistributedCache(Microsoft.Extensions.Caching.Distributed.IDistributedCache cache, IClock clock) | ||
{ | ||
_underlyingCache = cache; | ||
_clock = clock; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated by @Skrypt we don't need to define a new interface
Here we could inject IOptions<RedisCacheOptions> optionsAccessor
so that we can create an inner _redisCache
without having to resolve it, just by doing _redisCache = new RedisCache(optionsAccessor);
https://github.com/dotnet/aspnetcore/blob/db4dc17779dbfbe96b94ad33bc02a368b4a94463/src/Caching/StackExchangeRedis/src/RedisCache.cs#L49
Then, in the redis module startup, not call AddStackExchangeRedisCache()
that registers the RedisCache
https://github.com/dotnet/aspnetcore/blob/db4dc17779dbfbe96b94ad33bc02a368b4a94463/src/Caching/StackExchangeRedis/src/StackExchangeRedisCacheServiceCollectionExtensions.cs#L23-L40
And do what AddStackExchangeRedisCache()
does unless that we register our wrapper
services.AddOptions(); // <= not necessary here
services.Configure(setupAction); // <= idem, so I think only the following line
services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCacheService>());
Then we would have to implement IDisposable
so that we can dispose our inner _redisCache
.
public class RedisCacheService : IDistributedCache, IDisposable
Not sure yet of the best name ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the code doesn't actually depend on anything Redis specific what if I made it more generic?
The one problem I see with that is I don't have a 'clean room' code implementation of wrapping an existing dependency in services collection (I have an implementation I grabbed from somewhere else so the rights get tricky I think).
A semi dirty way around that would be to just wrap it in the DocumentManagers constructor in the if (distributedCache == typeof(MemoryCacheDistributedCache) {} block. I'm not the biggest fan of that though if someone can provide a clean services.AddDecorator(..) extension method then that would be preferred?
CheckDistributedErrorRetry(); | ||
|
||
if(_redisUnavailable) | ||
return Array.Empty<byte>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an .editorconfig
file at the root so that e.g. under Visual studio you get some warnings about code style rules. Here, always use braces {
and }
for if blocks and on new lines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .editorconfig file isn't working locally for me which is odd. It has the root=true and I've verified that my 'Follow project coding conventions' option is enabled.
if(_redisUnavailable) | ||
return Array.Empty<byte>(); | ||
|
||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open brace on new line
catch (Exception) | ||
{ | ||
RedisCacheFailed(); | ||
return Array.Empty<byte>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe return null
CheckDistributedErrorRetry(); | ||
|
||
if(_redisUnavailable) | ||
return Task.FromResult(Array.Empty<byte>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use braces and on newlines
if(_redisUnavailable) | ||
return Task.FromResult(Array.Empty<byte>()); | ||
|
||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open brace on new line
|
||
try { | ||
return _underlyingCache.GetAsync(key, token); | ||
} catch (Exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idem for all other ones
Just did a first review, will continue when I will have time |
return; | ||
|
||
// TODO: move to configuration or should this be an increasing number like RedisLock? | ||
_retryAt = _clock.UtcNow.AddSeconds(15); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I will think about it
I don't think it forces Redis BUT there isn't actually anything Redis specific in the code now so the naming/location might not make sense anymore. Originally I was thinking I'd want to look for RedisConnectionExceptions and RedisConnectionTImeouts but since that never ended up happening I could just rename/move things so that its a more generic DistributedCacheErrorWrapper or whatever better name someone can come up with? |
Closing as per #10313 (comment). |
As per the request in #10197 I've created an initial PR so we can discuss the changes necessary to improve system stability when Redis is unavailable.
I'm still unsure on the delay when an exception occurs. If it should move into config or if it should use an increasing time as per the RedisLock.