Skip to content

Commit 11ef77d

Browse files
authored
Automatically detect and tune for Azure Managed Redis caches (#2818)
Detect when connecting to a new Azure Managed Redis cache, and adjust default connection settings to align with their capabilities. More about Azure Managed Redis: https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-overview
1 parent 0071148 commit 11ef77d

File tree

4 files changed

+60
-38
lines changed

4 files changed

+60
-38
lines changed

docs/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Current package versions:
1010

1111
- Format IPv6 endpoints correctly when rewriting configration strings ([#2813 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2813))
1212
- Update default Redis version from 4.0.0 to 6.0.0 for Azure Redis resources ([#2810 by philon-msft](https://github.com/StackExchange/StackExchange.Redis/pull/2810))
13+
- Detect Azure Managed Redis caches and tune default connection settings for them ([#2818 by philon-msft](https://github.com/StackExchange/StackExchange.Redis/pull/2818))
1314

1415
## 2.8.16
1516

src/StackExchange.Redis/Configuration/AzureOptionsProvider.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,48 @@ public class AzureOptionsProvider : DefaultOptionsProvider
2121
public override Version DefaultVersion => RedisFeatures.v6_0_0;
2222

2323
/// <summary>
24-
/// List of domains known to be Azure Redis, so we can light up some helpful functionality
24+
/// Lists of domains known to be Azure Redis, so we can light up some helpful functionality
2525
/// for minimizing downtime during maintenance events and such.
2626
/// </summary>
2727
private static readonly string[] azureRedisDomains = new[]
2828
{
2929
".redis.cache.windows.net",
3030
".redis.cache.chinacloudapi.cn",
3131
".redis.cache.usgovcloudapi.net",
32-
".redis.cache.cloudapi.de",
3332
".redisenterprise.cache.azure.net",
3433
};
3534

35+
private static readonly string[] azureManagedRedisDomains = new[]
36+
{
37+
".redis.azure.net",
38+
".redis.chinacloudapi.cn",
39+
".redis.usgovcloudapi.net",
40+
};
41+
3642
/// <inheritdoc/>
3743
public override bool IsMatch(EndPoint endpoint)
3844
{
3945
if (endpoint is DnsEndPoint dnsEp)
4046
{
41-
foreach (var host in azureRedisDomains)
47+
if (IsHostInDomains(dnsEp.Host, azureRedisDomains) || IsHostInDomains(dnsEp.Host, azureManagedRedisDomains))
4248
{
43-
if (dnsEp.Host.EndsWith(host, StringComparison.InvariantCultureIgnoreCase))
44-
{
45-
return true;
46-
}
49+
return true;
4750
}
4851
}
52+
53+
return false;
54+
}
55+
56+
private bool IsHostInDomains(string hostName, string[] domains)
57+
{
58+
foreach (var domain in domains)
59+
{
60+
if (hostName.EndsWith(domain, StringComparison.InvariantCultureIgnoreCase))
61+
{
62+
return true;
63+
}
64+
}
65+
4966
return false;
5067
}
5168

@@ -65,6 +82,10 @@ public override bool GetDefaultSsl(EndPointCollection endPoints)
6582
{
6683
return true;
6784
}
85+
if (dns.Port == 10000 && IsHostInDomains(dns.Host, azureManagedRedisDomains))
86+
{
87+
return true; // SSL is enabled by default on AMR caches
88+
}
6889
break;
6990
case IPEndPoint ip:
7091
if (ip.Port == 6380)

tests/StackExchange.Redis.Tests/ConfigTests.cs

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Globalization;
34
using System.IO;
45
using System.IO.Pipelines;
@@ -25,7 +26,6 @@ public class ConfigTests : TestBase
2526
public ConfigTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
2627

2728
public Version DefaultVersion = new(3, 0, 0);
28-
public Version DefaultAzureVersion = new(6, 0, 0);
2929

3030
[Fact]
3131
public void ExpectedFields()
@@ -132,12 +132,21 @@ public void SslProtocols_InvalidValue()
132132
Assert.Throws<ArgumentOutOfRangeException>(() => ConfigurationOptions.Parse("myhost,sslProtocols=InvalidSslProtocol"));
133133
}
134134

135-
[Fact]
136-
public void ConfigurationOptionsDefaultForAzure()
137-
{
138-
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net");
139-
Assert.True(options.DefaultVersion.Equals(DefaultAzureVersion));
135+
[Theory]
136+
[InlineData("contoso.redis.cache.windows.net:6380", true)]
137+
[InlineData("contoso.REDIS.CACHE.chinacloudapi.cn:6380", true)] // added a few upper case chars to validate comparison
138+
[InlineData("contoso.redis.cache.usgovcloudapi.net:6380", true)]
139+
[InlineData("contoso.redisenterprise.cache.azure.net:10000", false)]
140+
[InlineData("contoso.redis.azure.net:10000", true)]
141+
[InlineData("contoso.redis.chinacloudapi.cn:10000", true)]
142+
[InlineData("contoso.redis.usgovcloudapi.net:10000", true)]
143+
public void ConfigurationOptionsDefaultForAzure(string hostAndPort, bool sslShouldBeEnabled)
144+
{
145+
Version defaultAzureVersion = new(6, 0, 0);
146+
var options = ConfigurationOptions.Parse(hostAndPort);
147+
Assert.True(options.DefaultVersion.Equals(defaultAzureVersion));
140148
Assert.False(options.AbortOnConnectFail);
149+
Assert.Equal(sslShouldBeEnabled, options.Ssl);
141150
}
142151

143152
[Fact]
@@ -148,31 +157,6 @@ public void ConfigurationOptionsForAzureWhenSpecified()
148157
Assert.True(options.AbortOnConnectFail);
149158
}
150159

151-
[Fact]
152-
public void ConfigurationOptionsDefaultForAzureChina()
153-
{
154-
// added a few upper case chars to validate comparison
155-
var options = ConfigurationOptions.Parse("contoso.REDIS.CACHE.chinacloudapi.cn");
156-
Assert.True(options.DefaultVersion.Equals(DefaultAzureVersion));
157-
Assert.False(options.AbortOnConnectFail);
158-
}
159-
160-
[Fact]
161-
public void ConfigurationOptionsDefaultForAzureGermany()
162-
{
163-
var options = ConfigurationOptions.Parse("contoso.redis.cache.cloudapi.de");
164-
Assert.True(options.DefaultVersion.Equals(DefaultAzureVersion));
165-
Assert.False(options.AbortOnConnectFail);
166-
}
167-
168-
[Fact]
169-
public void ConfigurationOptionsDefaultForAzureUSGov()
170-
{
171-
var options = ConfigurationOptions.Parse("contoso.redis.cache.usgovcloudapi.net");
172-
Assert.True(options.DefaultVersion.Equals(DefaultAzureVersion));
173-
Assert.False(options.AbortOnConnectFail);
174-
}
175-
176160
[Fact]
177161
public void ConfigurationOptionsDefaultForNonAzure()
178162
{

tests/StackExchange.Redis.Tests/DefaultOptionsTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Configuration;
34
using System.Linq;
45
using System.Net;
56
using System.Threading;
@@ -65,6 +66,21 @@ public void IsMatchOnDomain()
6566
Assert.IsType<DefaultOptionsProvider>(provider);
6667
}
6768

69+
[Theory]
70+
[InlineData("contoso.redis.cache.windows.net")]
71+
[InlineData("contoso.REDIS.CACHE.chinacloudapi.cn")] // added a few upper case chars to validate comparison
72+
[InlineData("contoso.redis.cache.usgovcloudapi.net")]
73+
[InlineData("contoso.redisenterprise.cache.azure.net")]
74+
[InlineData("contoso.redis.azure.net")]
75+
[InlineData("contoso.redis.chinacloudapi.cn")]
76+
[InlineData("contoso.redis.usgovcloudapi.net")]
77+
public void IsMatchOnAzureDomain(string hostName)
78+
{
79+
var epc = new EndPointCollection(new List<EndPoint>() { new DnsEndPoint(hostName, 0) });
80+
var provider = DefaultOptionsProvider.GetProvider(epc);
81+
Assert.IsType<AzureOptionsProvider>(provider);
82+
}
83+
6884
[Fact]
6985
public void AllOverridesFromDefaultsProp()
7086
{

0 commit comments

Comments
 (0)