Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit e6c7439

Browse files
committed
Fix Pooled RedisManager tests and add new RedisManagerPoolTests
1 parent 1035774 commit e6c7439

3 files changed

Lines changed: 254 additions & 1 deletion

File tree

tests/ServiceStack.Redis.Tests/PooledRedisClientManagerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ private void SetupRedisFactoryMock()
3838
mockFactory = new Mock<IRedisClientFactory>();
3939
mockFactory.Expect(x => x.CreateRedisClient(
4040
It.IsAny<RedisEndpoint>()))
41-
.Returns((Func<string, int, RedisClient>)((host, port) => new RedisClient(host, port)));
41+
.Returns((Func<RedisEndpoint, RedisClient>)((config) => new RedisClient(config)));
4242
}
4343

4444
public PooledRedisClientManager CreateManager(
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Threading;
5+
using Moq;
6+
using NUnit.Framework;
7+
using ServiceStack.Text;
8+
9+
namespace ServiceStack.Redis.Tests
10+
{
11+
[TestFixture, Category("Integration")]
12+
public class RedisManagerPoolTests
13+
{
14+
readonly string[] hosts = new[] {
15+
"readwrite1", "readwrite2:6000", "192.168.0.1", "localhost"
16+
};
17+
18+
readonly string[] testReadOnlyHosts = new[] {
19+
"read1", "read2:7000", "127.0.0.1"
20+
};
21+
22+
private string firstReadWriteHost;
23+
private string firstReadOnlyHost;
24+
25+
private Mock<IRedisClientFactory> mockFactory;
26+
27+
[SetUp]
28+
public void OnBeforeEachTest()
29+
{
30+
firstReadWriteHost = hosts[0];
31+
firstReadOnlyHost = testReadOnlyHosts[0];
32+
33+
SetupRedisFactoryMock();
34+
}
35+
36+
private void SetupRedisFactoryMock()
37+
{
38+
mockFactory = new Mock<IRedisClientFactory>();
39+
mockFactory.Expect(x => x.CreateRedisClient(
40+
It.IsAny<RedisEndpoint>()))
41+
.Returns((Func<RedisEndpoint, RedisClient>)((config) => new RedisClient(config)));
42+
}
43+
44+
public RedisManagerPool CreateManager(
45+
IRedisClientFactory usingFactory, params string[] hosts)
46+
{
47+
return new RedisManagerPool(hosts)
48+
{
49+
RedisClientFactory = usingFactory,
50+
};
51+
}
52+
53+
public RedisManagerPool CreateManager(params string[] hosts)
54+
{
55+
return CreateManager(mockFactory.Object, hosts);
56+
}
57+
58+
public RedisManagerPool CreateManager()
59+
{
60+
return CreateManager(mockFactory.Object, hosts);
61+
}
62+
63+
[Test]
64+
public void Can_change_db_for_client()
65+
{
66+
using (var db1 = new RedisManagerPool(TestConfig.SingleHost + "?db=1"))
67+
using (var db2 = new RedisManagerPool(TestConfig.SingleHost + "?db=2"))
68+
{
69+
var val = Environment.TickCount;
70+
var key = "test" + val;
71+
var db1c = db1.GetClient();
72+
var db2c = db2.GetClient();
73+
try
74+
{
75+
db1c.Set(key, val);
76+
Assert.That(db2c.Get<int>(key), Is.EqualTo(0));
77+
Assert.That(db1c.Get<int>(key), Is.EqualTo(val));
78+
}
79+
finally
80+
{
81+
db1c.Remove(key);
82+
}
83+
}
84+
}
85+
86+
[Test]
87+
public void Can_get_ReadWrite_client()
88+
{
89+
using (var manager = CreateManager())
90+
{
91+
var client = manager.GetClient();
92+
93+
AssertClientHasHost(client, firstReadWriteHost);
94+
95+
mockFactory.VerifyAll();
96+
}
97+
}
98+
99+
private static void AssertClientHasHost(IRedisClient client, string hostWithOptionalPort)
100+
{
101+
var parts = hostWithOptionalPort.Split(':');
102+
var port = parts.Length > 1 ? int.Parse(parts[1]) : RedisNativeClient.DefaultPort;
103+
104+
Assert.That(client.Host, Is.EqualTo(parts[0]));
105+
Assert.That(client.Port, Is.EqualTo(port));
106+
}
107+
108+
109+
[Test]
110+
public void Does_loop_through_ReadWrite_hosts()
111+
{
112+
using (var manager = CreateManager())
113+
{
114+
var client1 = manager.GetClient();
115+
client1.Dispose();
116+
var client2 = manager.GetClient();
117+
var client3 = manager.GetClient();
118+
var client4 = manager.GetClient();
119+
var client5 = manager.GetClient();
120+
121+
AssertClientHasHost(client1, hosts[0]);
122+
AssertClientHasHost(client2, hosts[1]);
123+
AssertClientHasHost(client3, hosts[2]);
124+
AssertClientHasHost(client4, hosts[3]);
125+
AssertClientHasHost(client5, hosts[0]);
126+
127+
mockFactory.VerifyAll();
128+
}
129+
}
130+
131+
[Test]
132+
public void Can_have_different_pool_size_and_host_configurations()
133+
{
134+
var writeHosts = new[] { "readwrite1" };
135+
136+
using (var manager = new RedisManagerPool(
137+
writeHosts,
138+
new RedisPoolConfig { MaxPoolSize = 4 })
139+
{
140+
RedisClientFactory = mockFactory.Object,
141+
}
142+
)
143+
{
144+
//A poolsize of 4 will not block getting 4 clients
145+
using (var client1 = manager.GetClient())
146+
using (var client2 = manager.GetClient())
147+
using (var client3 = manager.GetClient())
148+
using (var client4 = manager.GetClient())
149+
{
150+
AssertClientHasHost(client1, writeHosts[0]);
151+
AssertClientHasHost(client2, writeHosts[0]);
152+
AssertClientHasHost(client3, writeHosts[0]);
153+
AssertClientHasHost(client4, writeHosts[0]);
154+
}
155+
156+
mockFactory.VerifyAll();
157+
}
158+
}
159+
160+
[Test]
161+
public void Does_block_ReadWrite_clients_pool()
162+
{
163+
using (var manager = new RedisManagerPool(
164+
hosts,
165+
new RedisPoolConfig { MaxPoolSize = 4 })
166+
{
167+
RedisClientFactory = mockFactory.Object,
168+
}
169+
)
170+
{
171+
var delay = TimeSpan.FromSeconds(1);
172+
var client1 = manager.GetClient();
173+
var client2 = manager.GetClient();
174+
var client3 = manager.GetClient();
175+
var client4 = manager.GetClient();
176+
177+
Action func = delegate {
178+
Thread.Sleep(delay + TimeSpan.FromSeconds(0.5));
179+
client4.Dispose();
180+
};
181+
182+
func.BeginInvoke(null, null);
183+
184+
var start = DateTime.Now;
185+
186+
var client5 = manager.GetClient();
187+
188+
Assert.That(DateTime.Now - start, Is.GreaterThanOrEqualTo(delay));
189+
190+
AssertClientHasHost(client1, hosts[0]);
191+
AssertClientHasHost(client2, hosts[1]);
192+
AssertClientHasHost(client3, hosts[2]);
193+
AssertClientHasHost(client4, hosts[3]);
194+
AssertClientHasHost(client5, hosts[3]);
195+
196+
mockFactory.VerifyAll();
197+
}
198+
}
199+
200+
[Test]
201+
public void Can_support_64_threads_using_the_client_simultaneously()
202+
{
203+
const int noOfConcurrentClients = 64; //WaitHandle.WaitAll limit is <= 64
204+
var clientUsageMap = new Dictionary<string, int>();
205+
206+
var clientAsyncResults = new List<IAsyncResult>();
207+
using (var manager = CreateManager())
208+
{
209+
for (var i = 0; i < noOfConcurrentClients; i++)
210+
{
211+
var clientNo = i;
212+
var action = (Action)(() => UseClient(manager, clientNo, clientUsageMap));
213+
clientAsyncResults.Add(action.BeginInvoke(null, null));
214+
}
215+
}
216+
217+
WaitHandle.WaitAll(clientAsyncResults.ConvertAll(x => x.AsyncWaitHandle).ToArray());
218+
219+
Debug.WriteLine(TypeSerializer.SerializeToString(clientUsageMap));
220+
221+
var hostCount = 0;
222+
foreach (var entry in clientUsageMap)
223+
{
224+
Assert.That(entry.Value, Is.GreaterThanOrEqualTo(5), "Host has unproportianate distrobution: " + entry.Value);
225+
Assert.That(entry.Value, Is.LessThanOrEqualTo(30), "Host has unproportianate distrobution: " + entry.Value);
226+
hostCount += entry.Value;
227+
}
228+
229+
Assert.That(hostCount, Is.EqualTo(noOfConcurrentClients), "Invalid no of clients used");
230+
}
231+
232+
private static void UseClient(IRedisClientsManager manager, int clientNo, Dictionary<string, int> hostCountMap)
233+
{
234+
using (var client = manager.GetClient())
235+
{
236+
lock (hostCountMap)
237+
{
238+
int hostCount;
239+
if (!hostCountMap.TryGetValue(client.Host, out hostCount))
240+
{
241+
hostCount = 0;
242+
}
243+
244+
hostCountMap[client.Host] = ++hostCount;
245+
}
246+
247+
Debug.WriteLine(String.Format("Client '{0}' is using '{1}'", clientNo, client.Host));
248+
}
249+
}
250+
251+
}
252+
}

tests/ServiceStack.Redis.Tests/ServiceStack.Redis.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@
178178
<ItemGroup>
179179
<Compile Include="AdhocClientTests.cs" />
180180
<Compile Include="ConfigTests.cs" />
181+
<Compile Include="RedisManagerPoolTests.cs" />
181182
<Compile Include="DiagnosticTests.cs" />
182183
<Compile Include="Examples\ServiceStack_Redis_UseCase.cs" />
183184
<Compile Include="Generic\RedisClientListTestExtra.cs" />

0 commit comments

Comments
 (0)