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+ }
0 commit comments