Skip to content

Commit e1976ed

Browse files
committed
Add Redis connection factory and manager
`IRedisConnectionManager` is a singleton service responsible for initialising the connection to the Redis cluster and recreating it in the rare cases [1] where it fails to reconnect after a transient connection failure. It is based heavily on the `RedisConnection` class in the Azure Cache for Redis sample for ASP.NET Core [2] with a few notable differences: - `IRedisConnectionManager` is not responsible for providing any retry logic. Instead, consumers are expected to call `IRedisConnectionManager.CheckException` when an exception occurs making a call to Redis. This return value indicates whether the exception could have been caused by a transient connection error, which the caller can use to decide whether to perform a retry. - I've added separate configuration options (`DroppedConnectionGracePeriod` and `DroppedConnectionEpisodeTimeout`) for the two conditions checked against `RedisErrorThreshold` in the Azure Cache for Redis sample - `RedisConnectionManager` implements `IAsyncDisposable` instead of `IDisposable` I've abstracted the creation of Redis connections into a separate `IRedisConnectionFactory` service purely to facilitate unit testing of `RedisConnectionManager`. This interface is not intended to be consumed directly and is therefore internal to the assembly. [1] StackExchange/StackExchange.Redis#559 [2] https://github.com/Azure-Samples/azure-cache-redis-samples/blob/main/quickstart/aspnet-core/ContosoTeamStats/RedisConnection.cs
1 parent bf2b9dd commit e1976ed

13 files changed

+826
-0
lines changed

Buttercup.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buttercup.Application", "sr
3434
EndProject
3535
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buttercup.Application.Tests", "src\Buttercup.Application.Tests\Buttercup.Application.Tests.csproj", "{00B87344-63DF-4574-A5F6-F1BD5232033F}"
3636
EndProject
37+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buttercup.Redis", "src\Buttercup.Redis\Buttercup.Redis.csproj", "{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}"
38+
EndProject
39+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buttercup.Redis.Tests", "src\Buttercup.Redis.Tests\Buttercup.Redis.Tests.csproj", "{434E5794-262A-45AA-A972-BC43E41D5E15}"
40+
EndProject
3741
Global
3842
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3943
Debug|Any CPU = Debug|Any CPU
@@ -215,6 +219,30 @@ Global
215219
{00B87344-63DF-4574-A5F6-F1BD5232033F}.Release|x64.Build.0 = Release|Any CPU
216220
{00B87344-63DF-4574-A5F6-F1BD5232033F}.Release|x86.ActiveCfg = Release|Any CPU
217221
{00B87344-63DF-4574-A5F6-F1BD5232033F}.Release|x86.Build.0 = Release|Any CPU
222+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
223+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
224+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Debug|x64.ActiveCfg = Debug|Any CPU
225+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Debug|x64.Build.0 = Debug|Any CPU
226+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Debug|x86.ActiveCfg = Debug|Any CPU
227+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Debug|x86.Build.0 = Debug|Any CPU
228+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
229+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Release|Any CPU.Build.0 = Release|Any CPU
230+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Release|x64.ActiveCfg = Release|Any CPU
231+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Release|x64.Build.0 = Release|Any CPU
232+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Release|x86.ActiveCfg = Release|Any CPU
233+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC}.Release|x86.Build.0 = Release|Any CPU
234+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
235+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Debug|Any CPU.Build.0 = Debug|Any CPU
236+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Debug|x64.ActiveCfg = Debug|Any CPU
237+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Debug|x64.Build.0 = Debug|Any CPU
238+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Debug|x86.ActiveCfg = Debug|Any CPU
239+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Debug|x86.Build.0 = Debug|Any CPU
240+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Release|Any CPU.ActiveCfg = Release|Any CPU
241+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Release|Any CPU.Build.0 = Release|Any CPU
242+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Release|x64.ActiveCfg = Release|Any CPU
243+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Release|x64.Build.0 = Release|Any CPU
244+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Release|x86.ActiveCfg = Release|Any CPU
245+
{434E5794-262A-45AA-A972-BC43E41D5E15}.Release|x86.Build.0 = Release|Any CPU
218246
EndGlobalSection
219247
GlobalSection(NestedProjects) = preSolution
220248
{C1FBF99E-812B-4376-B483-73F708864933} = {309ABF0B-35CC-40DE-BCDF-E326309442D1}
@@ -231,5 +259,7 @@ Global
231259
{6984E338-14FA-421E-90B6-921AD05940EF} = {309ABF0B-35CC-40DE-BCDF-E326309442D1}
232260
{EE8E0C19-A969-4CEF-AB99-5C41A6B27990} = {309ABF0B-35CC-40DE-BCDF-E326309442D1}
233261
{00B87344-63DF-4574-A5F6-F1BD5232033F} = {309ABF0B-35CC-40DE-BCDF-E326309442D1}
262+
{E89C44A4-FA47-4EEE-A558-9EAE2944C5AC} = {309ABF0B-35CC-40DE-BCDF-E326309442D1}
263+
{434E5794-262A-45AA-A972-BC43E41D5E15} = {309ABF0B-35CC-40DE-BCDF-E326309442D1}
234264
EndGlobalSection
235265
EndGlobal
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<RootNamespace>Buttercup.Redis</RootNamespace>
4+
<IsPackable>false</IsPackable>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.2.0" />
8+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
9+
<PackageReference Include="Moq" Version="4.20.72" />
10+
<PackageReference Include="xunit" Version="2.9.3" />
11+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
12+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
13+
<PrivateAssets>all</PrivateAssets>
14+
</PackageReference>
15+
<PackageReference Include="coverlet.collector" Version="6.0.4">
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
17+
<PrivateAssets>all</PrivateAssets>
18+
</PackageReference>
19+
</ItemGroup>
20+
<ItemGroup>
21+
<ProjectReference Include="..\Buttercup.Redis\Buttercup.Redis.csproj" />
22+
</ItemGroup>
23+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.Extensions.Options;
2+
using StackExchange.Redis;
3+
using Xunit;
4+
5+
namespace Buttercup.Redis;
6+
7+
public sealed class RedisConnectionFactoryTests
8+
{
9+
#region NewConnection
10+
11+
[Fact]
12+
public async Task NewConnection_ReturnsNewConnection()
13+
{
14+
var options = new RedisConnectionOptions
15+
{
16+
ConnectionString = "localhost,abortConnect=false",
17+
};
18+
19+
var connectionFactory = new RedisConnectionFactory(Options.Create(options));
20+
21+
var connection = await connectionFactory.NewConnection();
22+
23+
Assert.Equal(
24+
ConfigurationOptions.Parse(options.ConnectionString).ToString(),
25+
connection.Configuration);
26+
}
27+
28+
#endregion
29+
}

0 commit comments

Comments
 (0)