1616
1717from enum import Enum
1818from time import perf_counter_ns , sleep
19- from typing import TYPE_CHECKING , Callable , Dict , List , Optional , Set , Tuple
19+ from typing import (TYPE_CHECKING , Callable , Dict , FrozenSet , List , Optional ,
20+ Sequence , Set , Tuple )
2021
2122if TYPE_CHECKING :
2223 from aws_advanced_python_wrapper .driver_dialect import DriverDialect
2930from aws_advanced_python_wrapper .host_availability import HostAvailability
3031from aws_advanced_python_wrapper .hostinfo import HostInfo , HostRole
3132from aws_advanced_python_wrapper .plugin import Plugin , PluginFactory
33+ from aws_advanced_python_wrapper .utils .accessible_regions import \
34+ parse as parse_accessible_regions
3235from aws_advanced_python_wrapper .utils .log import Logger
3336from aws_advanced_python_wrapper .utils .messages import Messages
3437from aws_advanced_python_wrapper .utils .properties import (Properties ,
@@ -106,6 +109,7 @@ def __init__(self, plugin_service: PluginService, props: Properties):
106109 self ._plugin_service : PluginService = plugin_service
107110 self ._rds_utils = RdsUtils ()
108111 self ._host_list_provider_service : Optional [HostListProviderService ] = None
112+ self ._accessible_regions : Optional [FrozenSet [str ]] = parse_accessible_regions (props )
109113
110114 self ._retry_delay_ms : int = WrapperProperties .OPEN_CONNECTION_RETRY_INTERVAL_MS .get_int (props )
111115 self ._open_connection_retry_timeout_ns : int = \
@@ -277,7 +281,7 @@ def _wait_for_topology_and_connect_to_instance(
277281 "AuroraInitialConnectionStrategyPlugin.WaitingForTopology" ,
278282 self ._wait_for_initial_topology_ms , original_connect_host .host )
279283
280- # Deviation from JDBC: force_monitoring_refresh_host_list takes seconds, and host list
284+ # force_monitoring_refresh_host_list takes seconds, and host list
281285 # providers without monitor support raise instead of returning their host list.
282286 timeout_sec = self ._wait_for_initial_topology_ms / 1000
283287 try :
@@ -326,7 +330,7 @@ def _get_instance_substitution_strategy(
326330 return InstanceSubstitutionStrategy .SUBSTITUTE_WITH_WRITER
327331
328332 if url_type == RdsUrlType .RDS_WRITER_CLUSTER :
329- writer = self ._get_writer ( )
333+ writer = self ._find_writer ( self . _plugin_service . all_hosts )
330334 if writer is None or not self ._rds_utils .is_rds_instance (writer .host ):
331335 return InstanceSubstitutionStrategy .DO_NOT_SUBSTITUTE
332336
@@ -405,7 +409,7 @@ def _get_role_to_verify(
405409 return HostRole .WRITER
406410
407411 if url_type == RdsUrlType .RDS_WRITER_CLUSTER :
408- writer = self ._get_writer ( )
412+ writer = self ._find_writer ( self . _plugin_service . all_hosts )
409413 if (writer is not None and self ._rds_utils .is_rds_instance (writer .host )
410414 and self ._rds_utils .is_same_region (writer .host , original_host )):
411415 # The cluster writer endpoint belongs to the same region as the current writer; it's active.
@@ -446,7 +450,12 @@ def _get_candidate_host(
446450 return original_connect_host
447451
448452 if substitution_strategy is InstanceSubstitutionStrategy .SUBSTITUTE_WITH_WRITER :
449- return self ._get_writer ()
453+ # Filter by accessible regions BEFORE picking the writer so a writer in
454+ # an unreachable region is never selected (no-op unless
455+ # gdb_accessible_regions is set on a Global Aurora dialect); the
456+ # candidate host is chosen from the filtered host list here.
457+ available_hosts = self ._filter_by_accessible_regions (self ._plugin_service .all_hosts )
458+ return self ._find_writer (available_hosts )
450459
451460 # SUBSTITUTE_WITH_ANY has no specific target role, so to_target_role() returns None
452461 target_role = substitution_strategy .to_target_role ()
@@ -457,17 +466,23 @@ def _get_candidate_host(
457466 "AuroraInitialConnectionStrategyPlugin.UnsupportedStrategy" , self ._selection_strategy ))
458467
459468 try :
469+ # Filter to accessible regions BEFORE any strategy/region selection so
470+ # a candidate is never chosen from an unreachable region (no-op unless
471+ # gdb_accessible_regions is set on a Global Aurora dialect).
472+ available_hosts = self ._filter_by_accessible_regions (self ._plugin_service .hosts )
473+
460474 aws_region = self ._rds_utils .get_rds_region (original_connect_host .host ) \
461475 if url_type .has_region else None
462476 if aws_region :
463477 hosts_in_region : List [HostInfo ] = [
464- host for host in self . _plugin_service . hosts
478+ host for host in available_hosts
465479 if (host_region := self ._rds_utils .get_rds_region (host .host )) is not None
466480 and aws_region .casefold () == host_region .casefold ()]
467481 return self ._plugin_service .get_host_info_by_strategy (
468482 target_role , self ._selection_strategy , hosts_in_region )
469483
470- return self ._plugin_service .get_host_info_by_strategy (target_role , self ._selection_strategy )
484+ return self ._plugin_service .get_host_info_by_strategy (
485+ target_role , self ._selection_strategy , available_hosts )
471486 except Exception :
472487 # Unable to find a candidate host.
473488 return None
@@ -479,8 +494,14 @@ def _set_initial_connection_host_info(
479494 and host_info is not None ):
480495 self ._host_list_provider_service .initial_connection_host_info = host_info
481496
482- def _get_writer (self ) -> Optional [HostInfo ]:
483- for host in self ._plugin_service .all_hosts :
497+ @staticmethod
498+ def _find_writer (hosts : Sequence [HostInfo ]) -> Optional [HostInfo ]:
499+ """Return the first WRITER in ``hosts``, or ``None``.
500+
501+ Does NOT filter by accessible regions — the caller decides whether to
502+ pass an already-filtered list.
503+ """
504+ for host in hosts :
484505 if host .role == HostRole .WRITER :
485506 return host
486507 return None
@@ -502,6 +523,21 @@ def _close_connection(self, connection: Optional[Connection]):
502523 def _delay (self , delay_ms : int ):
503524 sleep (delay_ms / 1000 )
504525
526+ def _filter_by_accessible_regions (self , hosts : Sequence [HostInfo ]) -> List [HostInfo ]:
527+ """Filter hosts down to the configured ``gdb_accessible_regions``.
528+
529+ Returns the list unchanged when no accessible-regions restriction is
530+ set. Filtering is delegated to the dialect's ``filter_available_hosts``
531+ (a no-op default; Global Aurora dialects filter by region), so this is a
532+ pass-through for non-Global clusters.
533+ """
534+ if self ._accessible_regions is None :
535+ return list (hosts )
536+ dialect = self ._plugin_service .database_dialect
537+ if dialect is None :
538+ return list (hosts )
539+ return dialect .filter_available_hosts (hosts , self ._accessible_regions )
540+
505541
506542class AuroraInitialConnectionStrategyPluginFactory (PluginFactory ):
507543 @staticmethod
0 commit comments