3535from bson .son import SON
3636from bson .tz_util import utc
3737import pymongo
38- from pymongo import auth , message
38+ from pymongo import auth , message , monitoring
3939from pymongo .common import CONNECT_TIMEOUT , _UUID_REPRESENTATIONS
4040from pymongo .command_cursor import CommandCursor
4141from pymongo .compression_support import _HAVE_SNAPPY , _HAVE_ZSTD
5858from pymongo .pool import SocketInfo , _METADATA
5959from pymongo .read_preferences import ReadPreference
6060from pymongo .server_description import ServerDescription
61- from pymongo .server_selectors import (any_server_selector ,
61+ from pymongo .server_selectors import (readable_server_selector ,
6262 writable_server_selector )
6363from pymongo .server_type import SERVER_TYPE
6464from pymongo .settings import TOPOLOGY_TYPE
7878from test .pymongo_mocks import MockClient
7979from test .utils import (assertRaisesExactly ,
8080 connected ,
81+ CMAPListener ,
8182 delay ,
8283 FunctionCallRecorder ,
8384 get_pool ,
@@ -454,21 +455,25 @@ def test_uri_security_options(self):
454455
455456class TestClient (IntegrationTest ):
456457
457- def test_max_idle_time_reaper (self ):
458+ def test_max_idle_time_reaper_default (self ):
458459 with client_knobs (kill_cursor_frequency = 0.1 ):
459460 # Assert reaper doesn't remove sockets when maxIdleTimeMS not set
460461 client = rs_or_single_client ()
461- server = client ._get_topology ().select_server (any_server_selector )
462+ server = client ._get_topology ().select_server (
463+ readable_server_selector )
462464 with server ._pool .get_socket ({}) as sock_info :
463465 pass
464466 self .assertEqual (1 , len (server ._pool .sockets ))
465467 self .assertTrue (sock_info in server ._pool .sockets )
466468 client .close ()
467469
470+ def test_max_idle_time_reaper_removes_stale_minPoolSize (self ):
471+ with client_knobs (kill_cursor_frequency = 0.1 ):
468472 # Assert reaper removes idle socket and replaces it with a new one
469473 client = rs_or_single_client (maxIdleTimeMS = 500 ,
470474 minPoolSize = 1 )
471- server = client ._get_topology ().select_server (any_server_selector )
475+ server = client ._get_topology ().select_server (
476+ readable_server_selector )
472477 with server ._pool .get_socket ({}) as sock_info :
473478 pass
474479 # When the reaper runs at the same time as the get_socket, two
@@ -480,11 +485,14 @@ def test_max_idle_time_reaper(self):
480485 "replace stale socket" )
481486 client .close ()
482487
488+ def test_max_idle_time_reaper_does_not_exceed_maxPoolSize (self ):
489+ with client_knobs (kill_cursor_frequency = 0.1 ):
483490 # Assert reaper respects maxPoolSize when adding new sockets.
484491 client = rs_or_single_client (maxIdleTimeMS = 500 ,
485492 minPoolSize = 1 ,
486493 maxPoolSize = 1 )
487- server = client ._get_topology ().select_server (any_server_selector )
494+ server = client ._get_topology ().select_server (
495+ readable_server_selector )
488496 with server ._pool .get_socket ({}) as sock_info :
489497 pass
490498 # When the reaper runs at the same time as the get_socket,
@@ -496,9 +504,12 @@ def test_max_idle_time_reaper(self):
496504 "replace stale socket" )
497505 client .close ()
498506
507+ def test_max_idle_time_reaper_removes_stale (self ):
508+ with client_knobs (kill_cursor_frequency = 0.1 ):
499509 # Assert reaper has removed idle socket and NOT replaced it
500510 client = rs_or_single_client (maxIdleTimeMS = 500 )
501- server = client ._get_topology ().select_server (any_server_selector )
511+ server = client ._get_topology ().select_server (
512+ readable_server_selector )
502513 with server ._pool .get_socket ({}) as sock_info_one :
503514 pass
504515 # Assert that the pool does not close sockets prematurely.
@@ -514,12 +525,14 @@ def test_max_idle_time_reaper(self):
514525 def test_min_pool_size (self ):
515526 with client_knobs (kill_cursor_frequency = .1 ):
516527 client = rs_or_single_client ()
517- server = client ._get_topology ().select_server (any_server_selector )
528+ server = client ._get_topology ().select_server (
529+ readable_server_selector )
518530 self .assertEqual (0 , len (server ._pool .sockets ))
519531
520532 # Assert that pool started up at minPoolSize
521533 client = rs_or_single_client (minPoolSize = 10 )
522- server = client ._get_topology ().select_server (any_server_selector )
534+ server = client ._get_topology ().select_server (
535+ readable_server_selector )
523536 wait_until (lambda : 10 == len (server ._pool .sockets ),
524537 "pool initialized with 10 sockets" )
525538
@@ -534,7 +547,8 @@ def test_max_idle_time_checkout(self):
534547 # Use high frequency to test _get_socket_no_auth.
535548 with client_knobs (kill_cursor_frequency = 99999999 ):
536549 client = rs_or_single_client (maxIdleTimeMS = 500 )
537- server = client ._get_topology ().select_server (any_server_selector )
550+ server = client ._get_topology ().select_server (
551+ readable_server_selector )
538552 with server ._pool .get_socket ({}) as sock_info :
539553 pass
540554 self .assertEqual (1 , len (server ._pool .sockets ))
@@ -548,7 +562,8 @@ def test_max_idle_time_checkout(self):
548562
549563 # Test that sockets are reused if maxIdleTimeMS is not set.
550564 client = rs_or_single_client ()
551- server = client ._get_topology ().select_server (any_server_selector )
565+ server = client ._get_topology ().select_server (
566+ readable_server_selector )
552567 with server ._pool .get_socket ({}) as sock_info :
553568 pass
554569 self .assertEqual (1 , len (server ._pool .sockets ))
@@ -2016,5 +2031,60 @@ def timeout_task():
20162031 self .assertIsNone (ct .get ())
20172032
20182033
2034+ class TestClientPool (MockClientTest ):
2035+
2036+ def test_rs_client_does_not_maintain_pool_to_arbiters (self ):
2037+ listener = CMAPListener ()
2038+ c = MockClient (
2039+ standalones = [],
2040+ members = ['a:1' , 'b:2' , 'c:3' , 'd:4' ],
2041+ mongoses = [],
2042+ arbiters = ['c:3' ], # c:3 is an arbiter.
2043+ down_hosts = ['d:4' ], # d:4 is unreachable.
2044+ host = ['a:1' , 'b:2' , 'c:3' , 'd:4' ],
2045+ replicaSet = 'rs' ,
2046+ minPoolSize = 1 , # minPoolSize
2047+ event_listeners = [listener ],
2048+ )
2049+ self .addCleanup (c .close )
2050+
2051+ wait_until (lambda : len (c .nodes ) == 3 , 'connect' )
2052+ self .assertEqual (c .address , ('a' , 1 ))
2053+ self .assertEqual (c .arbiters , set ([('c' , 3 )]))
2054+ # Assert that we create 2 and only 2 pooled connections.
2055+ listener .wait_for_event (monitoring .ConnectionReadyEvent , 2 )
2056+ self .assertEqual (
2057+ listener .event_count (monitoring .ConnectionCreatedEvent ), 2 )
2058+ # Assert that we do not create connections to arbiters.
2059+ arbiter = c ._topology .get_server_by_address (('c' , 3 ))
2060+ self .assertFalse (arbiter .pool .sockets )
2061+ # Assert that we do not create connections to unknown servers.
2062+ arbiter = c ._topology .get_server_by_address (('d' , 4 ))
2063+ self .assertFalse (arbiter .pool .sockets )
2064+
2065+ def test_direct_client_maintains_pool_to_arbiter (self ):
2066+ listener = CMAPListener ()
2067+ c = MockClient (
2068+ standalones = [],
2069+ members = ['a:1' , 'b:2' , 'c:3' ],
2070+ mongoses = [],
2071+ arbiters = ['c:3' ], # c:3 is an arbiter.
2072+ host = 'c:3' ,
2073+ directConnection = True ,
2074+ minPoolSize = 1 , # minPoolSize
2075+ event_listeners = [listener ],
2076+ )
2077+ self .addCleanup (c .close )
2078+
2079+ wait_until (lambda : len (c .nodes ) == 1 , 'connect' )
2080+ self .assertEqual (c .address , ('c' , 3 ))
2081+ # Assert that we create 1 pooled connection.
2082+ listener .wait_for_event (monitoring .ConnectionReadyEvent , 1 )
2083+ self .assertEqual (
2084+ listener .event_count (monitoring .ConnectionCreatedEvent ), 1 )
2085+ arbiter = c ._topology .get_server_by_address (('c' , 3 ))
2086+ self .assertEqual (len (arbiter .pool .sockets ), 1 )
2087+
2088+
20192089if __name__ == "__main__" :
20202090 unittest .main ()
0 commit comments