17
17
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
18
# See the License for the specific language governing permissions and
19
19
# limitations under the License.
20
-
20
+ from collections import OrderedDict
21
21
from unittest import TestCase
22
22
23
23
from neo4j .bolt import ProtocolError
24
24
from neo4j .bolt .connection import connect
25
- from neo4j .v1 .routing import OrderedSet , RoutingTable , RoutingConnectionPool
25
+ from neo4j .v1 .routing import OrderedSet , RoutingTable , RoutingConnectionPool , LeastConnectedLoadBalancingStrategy , \
26
+ RoundRobinLoadBalancingStrategy
26
27
from neo4j .v1 .security import basic_auth
27
- from neo4j .v1 .api import Driver , READ_ACCESS , WRITE_ACCESS
28
+ from neo4j .v1 .api import READ_ACCESS , WRITE_ACCESS
28
29
29
30
30
31
VALID_ROUTING_RECORD = {
@@ -56,7 +57,6 @@ def connector(address):
56
57
57
58
58
59
class RoundRobinSetTestCase (TestCase ):
59
-
60
60
def test_should_repr_as_set (self ):
61
61
s = OrderedSet ([1 , 2 , 3 ])
62
62
assert repr (s ) == "{1, 2, 3}"
@@ -135,15 +135,13 @@ def test_should_be_able_to_replace(self):
135
135
136
136
137
137
class RoutingTableConstructionTestCase (TestCase ):
138
-
139
138
def test_should_be_initially_stale (self ):
140
139
table = RoutingTable ()
141
140
assert not table .is_fresh (READ_ACCESS )
142
141
assert not table .is_fresh (WRITE_ACCESS )
143
142
144
143
145
144
class RoutingTableParseRoutingInfoTestCase (TestCase ):
146
-
147
145
def test_should_return_routing_table_on_valid_record (self ):
148
146
table = RoutingTable .parse_routing_info ([VALID_ROUTING_RECORD ])
149
147
assert table .routers == {('127.0.0.1' , 9001 ), ('127.0.0.1' , 9002 ), ('127.0.0.1' , 9003 )}
@@ -172,7 +170,6 @@ def test_should_fail_on_multiple_records(self):
172
170
173
171
174
172
class RoutingTableFreshnessTestCase (TestCase ):
175
-
176
173
def test_should_be_fresh_after_update (self ):
177
174
table = RoutingTable .parse_routing_info ([VALID_ROUTING_RECORD ])
178
175
assert table .is_fresh (READ_ACCESS )
@@ -198,7 +195,6 @@ def test_should_become_stale_if_no_writers(self):
198
195
199
196
200
197
class RoutingTableUpdateTestCase (TestCase ):
201
-
202
198
def setUp (self ):
203
199
self .table = RoutingTable (
204
200
[("192.168.1.1" , 7687 ), ("192.168.1.2" , 7687 )], [("192.168.1.3" , 7687 )], [], 0 )
@@ -224,9 +220,119 @@ def test_update_should_replace_ttl(self):
224
220
225
221
226
222
class RoutingConnectionPoolConstructionTestCase (TestCase ):
227
-
228
223
def test_should_populate_initial_router (self ):
229
224
initial_router = ("127.0.0.1" , 9001 )
230
225
router = ("127.0.0.1" , 9002 )
231
226
with RoutingConnectionPool (connector , initial_router , {}, router ) as pool :
232
227
assert pool .routing_table .routers == {("127.0.0.1" , 9002 )}
228
+
229
+
230
+ class FakeConnectionPool (object ):
231
+
232
+ def __init__ (self , addresses ):
233
+ self ._addresses = addresses
234
+
235
+ def in_use_connection_count (self , address ):
236
+ return self ._addresses .get (address , 0 )
237
+
238
+
239
+ class RoundRobinLoadBalancingStrategyTestCase (TestCase ):
240
+
241
+ def test_simple_reader_selection (self ):
242
+ strategy = RoundRobinLoadBalancingStrategy ()
243
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "0.0.0.0" )
244
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "1.1.1.1" )
245
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "2.2.2.2" )
246
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "0.0.0.0" )
247
+
248
+ def test_empty_reader_selection (self ):
249
+ strategy = RoundRobinLoadBalancingStrategy ()
250
+ self .assertIsNone (strategy .select_reader ([]))
251
+
252
+ def test_simple_writer_selection (self ):
253
+ strategy = RoundRobinLoadBalancingStrategy ()
254
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "0.0.0.0" )
255
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "1.1.1.1" )
256
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "2.2.2.2" )
257
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "0.0.0.0" )
258
+
259
+ def test_empty_writer_selection (self ):
260
+ strategy = RoundRobinLoadBalancingStrategy ()
261
+ self .assertIsNone (strategy .select_writer ([]))
262
+
263
+
264
+ class LeastConnectedLoadBalancingStrategyTestCase (TestCase ):
265
+
266
+ def test_simple_reader_selection (self ):
267
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
268
+ ("0.0.0.0" , 2 ),
269
+ ("1.1.1.1" , 1 ),
270
+ ("2.2.2.2" , 0 ),
271
+ ])))
272
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "2.2.2.2" )
273
+
274
+ def test_reader_selection_with_clash (self ):
275
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
276
+ ("0.0.0.0" , 0 ),
277
+ ("0.0.0.1" , 0 ),
278
+ ("1.1.1.1" , 1 ),
279
+ ])))
280
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "0.0.0.1" , "1.1.1.1" ]), "0.0.0.0" )
281
+ self .assertEqual (strategy .select_reader (["0.0.0.0" , "0.0.0.1" , "1.1.1.1" ]), "0.0.0.1" )
282
+
283
+ def test_empty_reader_selection (self ):
284
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
285
+ ])))
286
+ self .assertIsNone (strategy .select_reader ([]))
287
+
288
+ def test_not_in_pool_reader_selection (self ):
289
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
290
+ ("1.1.1.1" , 1 ),
291
+ ("2.2.2.2" , 2 ),
292
+ ])))
293
+ self .assertEqual (strategy .select_reader (["2.2.2.2" , "3.3.3.3" ]), "3.3.3.3" )
294
+
295
+ def test_partially_in_pool_reader_selection (self ):
296
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
297
+ ("1.1.1.1" , 1 ),
298
+ ("2.2.2.2" , 0 ),
299
+ ])))
300
+ self .assertEqual (strategy .select_reader (["2.2.2.2" , "3.3.3.3" ]), "2.2.2.2" )
301
+ self .assertEqual (strategy .select_reader (["2.2.2.2" , "3.3.3.3" ]), "3.3.3.3" )
302
+
303
+ def test_simple_writer_selection (self ):
304
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
305
+ ("0.0.0.0" , 2 ),
306
+ ("1.1.1.1" , 1 ),
307
+ ("2.2.2.2" , 0 ),
308
+ ])))
309
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "1.1.1.1" , "2.2.2.2" ]), "2.2.2.2" )
310
+
311
+ def test_writer_selection_with_clash (self ):
312
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
313
+ ("0.0.0.0" , 0 ),
314
+ ("0.0.0.1" , 0 ),
315
+ ("1.1.1.1" , 1 ),
316
+ ])))
317
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "0.0.0.1" , "1.1.1.1" ]), "0.0.0.0" )
318
+ self .assertEqual (strategy .select_writer (["0.0.0.0" , "0.0.0.1" , "1.1.1.1" ]), "0.0.0.1" )
319
+
320
+ def test_empty_writer_selection (self ):
321
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
322
+ ])))
323
+ self .assertIsNone (strategy .select_writer ([]))
324
+
325
+ def test_not_in_pool_writer_selection (self ):
326
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
327
+ ("1.1.1.1" , 1 ),
328
+ ("2.2.2.2" , 2 ),
329
+ ])))
330
+ self .assertEqual (strategy .select_writer (["2.2.2.2" , "3.3.3.3" ]), "3.3.3.3" )
331
+
332
+ def test_partially_in_pool_writer_selection (self ):
333
+ strategy = LeastConnectedLoadBalancingStrategy (FakeConnectionPool (OrderedDict ([
334
+ ("1.1.1.1" , 1 ),
335
+ ("2.2.2.2" , 0 ),
336
+ ])))
337
+ self .assertEqual (strategy .select_writer (["2.2.2.2" , "3.3.3.3" ]), "2.2.2.2" )
338
+ self .assertEqual (strategy .select_writer (["2.2.2.2" , "3.3.3.3" ]), "3.3.3.3" )
0 commit comments