Skip to content

Commit

Permalink
♻ refactor(IDistributedCache): Optimize connections under Isolation a…
Browse files Browse the repository at this point in the history
…nd Attempt to reconnect (#740)

* ♻ refactor(IDistributedCache): Optimize connections under Isolation and Attempt to reconnect

* chore: TargetFrameworks

* refactor: Code optimization

* chore: TargetFrameworks

* ♻ refactor(IDistributedCache): Support multi tenant

* ♻ refactor(IDistributedCache): Code smell

* ♻ refactor(IDistributedCache): Adjust the GeneratedKey method
  • Loading branch information
wzh425 authored Nov 25, 2024
1 parent 0aa53ec commit 433b3eb
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) MASA Stack All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

namespace Masa.BuildingBlocks.Caching;

internal class DistributedCacheClientCache
{
private static ConcurrentDictionary<string, IManualDistributedCacheClient> _cacheClients = new();

public IManualDistributedCacheClient GetCacheClient(IServiceProvider serviceProvider)
{
var environment = GetCurrentEnvironment(serviceProvider);
var tenantId = GetCurrentTenantId(serviceProvider);

var key = GenerateKey(environment, tenantId);

return _cacheClients.GetOrAdd(key, _ => CreateCacheClient(serviceProvider));
}

private static string GetCurrentEnvironment(IServiceProvider serviceProvider)
{
var multiEnvironmentContext = serviceProvider.GetService<IMultiEnvironmentContext>();
return multiEnvironmentContext?.CurrentEnvironment ?? string.Empty;
}

private static string GetCurrentTenantId(IServiceProvider serviceProvider)
{
var multiTenantContext = serviceProvider.GetService<IMultiTenantContext>();
return multiTenantContext?.CurrentTenant?.Id ?? string.Empty;
}

private static string GenerateKey(string environment, string tenantId)
{
if (string.IsNullOrEmpty(environment))
{
return tenantId;
}
if (string.IsNullOrEmpty(tenantId))
{
return environment;
}
return $"{environment}:{tenantId}";
}

private static IManualDistributedCacheClient CreateCacheClient(IServiceProvider serviceProvider)
{
try
{
var scopedService = serviceProvider.GetRequiredService<ScopedService<IManualDistributedCacheClient>>();
return scopedService.Service;
}
catch (Exception ex)
{
throw new InvalidOperationException("Failed to create cache client", ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ public static void TryAddDistributedCache(
services.TryAddTransient<IManualDistributedCacheClient>(serviceProvider =>
{
var cacheClient = serviceProvider.EnableIsolation() ?
serviceProvider.GetRequiredService<ScopedService<IManualDistributedCacheClient>>().Service :
serviceProvider.GetRequiredService<DistributedCacheClientCache>().GetCacheClient(serviceProvider) :
serviceProvider.GetRequiredService<SingletonService<IManualDistributedCacheClient>>().Service;

return new DefaultDistributedCacheClient(cacheClient);
});
services.TryAddTransient<IDistributedCacheClient>(serviceProvider
Expand Down Expand Up @@ -65,6 +66,8 @@ private static void AddTypeAlias(

private static void AddCaching(this IServiceCollection services)
{
services.TryAddSingleton<DistributedCacheClientCache>();

services.TryAddSingleton<SingletonService<IManualDistributedCacheClient>>(serviceProvider =>
new SingletonService<IManualDistributedCacheClient>(serviceProvider.GetRequiredService<IDistributedCacheClientFactory>()
.Create()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,31 @@ public abstract class RedisCacheClientBase : DistributedCacheClientBase
{
protected readonly string? InstanceId;
protected static readonly Guid UniquelyIdentifies = Guid.NewGuid();
protected readonly ISubscriber Subscriber;
protected ISubscriber Subscriber;

protected IDatabase Db
{
get
{
if (_connection.IsConnected || _connection.IsConnecting)
return _connection.GetDatabase();
EnsureDbConnection();

throw new NotSupportedException("Redis service has been disconnected, please wait for reconnection and try again");
return _connection.GetDatabase();
}
}

private readonly IConnectionMultiplexer _connection;
private IConnectionMultiplexer _connection;
protected readonly JsonSerializerOptions GlobalJsonSerializerOptions;
private readonly CacheEntryOptions _globalCacheEntryOptions;
private readonly CacheOptions _globalCacheOptions;

private readonly RedisConfigurationOptions _redisConfigurationOptions;

protected RedisCacheClientBase(
RedisConfigurationOptions redisConfigurationOptions,
JsonSerializerOptions? jsonSerializerOptions)
: this(redisConfigurationOptions.GlobalCacheOptions, redisConfigurationOptions, jsonSerializerOptions)
{
_redisConfigurationOptions = redisConfigurationOptions;
var redisConfiguration = redisConfigurationOptions.GetAvailableRedisOptions();
_connection = ConnectionMultiplexer.Connect(redisConfiguration);
Subscriber = _connection.GetSubscriber();
Expand All @@ -51,6 +53,22 @@ private RedisCacheClientBase(
GlobalJsonSerializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions().EnableDynamicTypes();
}

protected void EnsureDbConnection()
{
if (!_connection.IsConnected && !_connection.IsConnecting)
{
// Attempt to reconnect
var redisConfiguration = _redisConfigurationOptions.GetAvailableRedisOptions();
_connection = ConnectionMultiplexer.Connect(redisConfiguration);
Subscriber = _connection.GetSubscriber();
}

if (!_connection.IsConnected && !_connection.IsConnecting)
{
throw new NotSupportedException("Unable to reconnect to Redis, please check the connection settings and try again.");
}
}

protected T? ConvertToValue<T>(RedisValue value, out bool isExist)
{
if (value is { HasValue: true, IsNullOrEmpty: false })
Expand Down

0 comments on commit 433b3eb

Please sign in to comment.