35
35
from bson .son import SON
36
36
from bson .tz_util import utc
37
37
import pymongo
38
- from pymongo import auth , message
38
+ from pymongo import auth , message , monitoring
39
39
from pymongo .common import CONNECT_TIMEOUT , _UUID_REPRESENTATIONS
40
40
from pymongo .command_cursor import CommandCursor
41
41
from pymongo .compression_support import _HAVE_SNAPPY , _HAVE_ZSTD
58
58
from pymongo .pool import SocketInfo , _METADATA
59
59
from pymongo .read_preferences import ReadPreference
60
60
from pymongo .server_description import ServerDescription
61
- from pymongo .server_selectors import (any_server_selector ,
61
+ from pymongo .server_selectors import (readable_server_selector ,
62
62
writable_server_selector )
63
63
from pymongo .server_type import SERVER_TYPE
64
64
from pymongo .settings import TOPOLOGY_TYPE
76
76
from test .pymongo_mocks import MockClient
77
77
from test .utils import (assertRaisesExactly ,
78
78
connected ,
79
+ CMAPListener ,
79
80
delay ,
80
81
FunctionCallRecorder ,
81
82
get_pool ,
@@ -452,21 +453,25 @@ def test_uri_security_options(self):
452
453
453
454
class TestClient (IntegrationTest ):
454
455
455
- def test_max_idle_time_reaper (self ):
456
+ def test_max_idle_time_reaper_default (self ):
456
457
with client_knobs (kill_cursor_frequency = 0.1 ):
457
458
# Assert reaper doesn't remove sockets when maxIdleTimeMS not set
458
459
client = rs_or_single_client ()
459
- server = client ._get_topology ().select_server (any_server_selector )
460
+ server = client ._get_topology ().select_server (
461
+ readable_server_selector )
460
462
with server ._pool .get_socket ({}) as sock_info :
461
463
pass
462
464
self .assertEqual (1 , len (server ._pool .sockets ))
463
465
self .assertTrue (sock_info in server ._pool .sockets )
464
466
client .close ()
465
467
468
+ def test_max_idle_time_reaper_removes_stale_minPoolSize (self ):
469
+ with client_knobs (kill_cursor_frequency = 0.1 ):
466
470
# Assert reaper removes idle socket and replaces it with a new one
467
471
client = rs_or_single_client (maxIdleTimeMS = 500 ,
468
472
minPoolSize = 1 )
469
- server = client ._get_topology ().select_server (any_server_selector )
473
+ server = client ._get_topology ().select_server (
474
+ readable_server_selector )
470
475
with server ._pool .get_socket ({}) as sock_info :
471
476
pass
472
477
# When the reaper runs at the same time as the get_socket, two
@@ -478,11 +483,14 @@ def test_max_idle_time_reaper(self):
478
483
"replace stale socket" )
479
484
client .close ()
480
485
486
+ def test_max_idle_time_reaper_does_not_exceed_maxPoolSize (self ):
487
+ with client_knobs (kill_cursor_frequency = 0.1 ):
481
488
# Assert reaper respects maxPoolSize when adding new sockets.
482
489
client = rs_or_single_client (maxIdleTimeMS = 500 ,
483
490
minPoolSize = 1 ,
484
491
maxPoolSize = 1 )
485
- server = client ._get_topology ().select_server (any_server_selector )
492
+ server = client ._get_topology ().select_server (
493
+ readable_server_selector )
486
494
with server ._pool .get_socket ({}) as sock_info :
487
495
pass
488
496
# When the reaper runs at the same time as the get_socket,
@@ -494,9 +502,12 @@ def test_max_idle_time_reaper(self):
494
502
"replace stale socket" )
495
503
client .close ()
496
504
505
+ def test_max_idle_time_reaper_removes_stale (self ):
506
+ with client_knobs (kill_cursor_frequency = 0.1 ):
497
507
# Assert reaper has removed idle socket and NOT replaced it
498
508
client = rs_or_single_client (maxIdleTimeMS = 500 )
499
- server = client ._get_topology ().select_server (any_server_selector )
509
+ server = client ._get_topology ().select_server (
510
+ readable_server_selector )
500
511
with server ._pool .get_socket ({}) as sock_info_one :
501
512
pass
502
513
# Assert that the pool does not close sockets prematurely.
@@ -512,12 +523,14 @@ def test_max_idle_time_reaper(self):
512
523
def test_min_pool_size (self ):
513
524
with client_knobs (kill_cursor_frequency = .1 ):
514
525
client = rs_or_single_client ()
515
- server = client ._get_topology ().select_server (any_server_selector )
526
+ server = client ._get_topology ().select_server (
527
+ readable_server_selector )
516
528
self .assertEqual (0 , len (server ._pool .sockets ))
517
529
518
530
# Assert that pool started up at minPoolSize
519
531
client = rs_or_single_client (minPoolSize = 10 )
520
- server = client ._get_topology ().select_server (any_server_selector )
532
+ server = client ._get_topology ().select_server (
533
+ readable_server_selector )
521
534
wait_until (lambda : 10 == len (server ._pool .sockets ),
522
535
"pool initialized with 10 sockets" )
523
536
@@ -532,7 +545,8 @@ def test_max_idle_time_checkout(self):
532
545
# Use high frequency to test _get_socket_no_auth.
533
546
with client_knobs (kill_cursor_frequency = 99999999 ):
534
547
client = rs_or_single_client (maxIdleTimeMS = 500 )
535
- server = client ._get_topology ().select_server (any_server_selector )
548
+ server = client ._get_topology ().select_server (
549
+ readable_server_selector )
536
550
with server ._pool .get_socket ({}) as sock_info :
537
551
pass
538
552
self .assertEqual (1 , len (server ._pool .sockets ))
@@ -546,7 +560,8 @@ def test_max_idle_time_checkout(self):
546
560
547
561
# Test that sockets are reused if maxIdleTimeMS is not set.
548
562
client = rs_or_single_client ()
549
- server = client ._get_topology ().select_server (any_server_selector )
563
+ server = client ._get_topology ().select_server (
564
+ readable_server_selector )
550
565
with server ._pool .get_socket ({}) as sock_info :
551
566
pass
552
567
self .assertEqual (1 , len (server ._pool .sockets ))
@@ -2008,5 +2023,60 @@ def timeout_task():
2008
2023
self .assertIsNone (ct .get ())
2009
2024
2010
2025
2026
+ class TestClientPool (MockClientTest ):
2027
+
2028
+ def test_rs_client_does_not_maintain_pool_to_arbiters (self ):
2029
+ listener = CMAPListener ()
2030
+ c = MockClient (
2031
+ standalones = [],
2032
+ members = ['a:1' , 'b:2' , 'c:3' , 'd:4' ],
2033
+ mongoses = [],
2034
+ arbiters = ['c:3' ], # c:3 is an arbiter.
2035
+ down_hosts = ['d:4' ], # d:4 is unreachable.
2036
+ host = ['a:1' , 'b:2' , 'c:3' , 'd:4' ],
2037
+ replicaSet = 'rs' ,
2038
+ minPoolSize = 1 , # minPoolSize
2039
+ event_listeners = [listener ],
2040
+ )
2041
+ self .addCleanup (c .close )
2042
+
2043
+ wait_until (lambda : len (c .nodes ) == 3 , 'connect' )
2044
+ self .assertEqual (c .address , ('a' , 1 ))
2045
+ self .assertEqual (c .arbiters , set ([('c' , 3 )]))
2046
+ # Assert that we create 2 and only 2 pooled connections.
2047
+ listener .wait_for_event (monitoring .ConnectionReadyEvent , 2 )
2048
+ self .assertEqual (
2049
+ listener .event_count (monitoring .ConnectionCreatedEvent ), 2 )
2050
+ # Assert that we do not create connections to arbiters.
2051
+ arbiter = c ._topology .get_server_by_address (('c' , 3 ))
2052
+ self .assertFalse (arbiter .pool .sockets )
2053
+ # Assert that we do not create connections to unknown servers.
2054
+ arbiter = c ._topology .get_server_by_address (('d' , 4 ))
2055
+ self .assertFalse (arbiter .pool .sockets )
2056
+
2057
+ def test_direct_client_maintains_pool_to_arbiter (self ):
2058
+ listener = CMAPListener ()
2059
+ c = MockClient (
2060
+ standalones = [],
2061
+ members = ['a:1' , 'b:2' , 'c:3' ],
2062
+ mongoses = [],
2063
+ arbiters = ['c:3' ], # c:3 is an arbiter.
2064
+ host = 'c:3' ,
2065
+ directConnection = True ,
2066
+ minPoolSize = 1 , # minPoolSize
2067
+ event_listeners = [listener ],
2068
+ )
2069
+ self .addCleanup (c .close )
2070
+
2071
+ wait_until (lambda : len (c .nodes ) == 1 , 'connect' )
2072
+ self .assertEqual (c .address , ('c' , 3 ))
2073
+ # Assert that we create 1 pooled connection.
2074
+ listener .wait_for_event (monitoring .ConnectionReadyEvent , 1 )
2075
+ self .assertEqual (
2076
+ listener .event_count (monitoring .ConnectionCreatedEvent ), 1 )
2077
+ arbiter = c ._topology .get_server_by_address (('c' , 3 ))
2078
+ self .assertEqual (len (arbiter .pool .sockets ), 1 )
2079
+
2080
+
2011
2081
if __name__ == "__main__" :
2012
2082
unittest .main ()
0 commit comments