Skip to content

Commit fca73dc

Browse files
committed
chore: gdb regions
1 parent 16070b5 commit fca73dc

25 files changed

Lines changed: 3405 additions & 43 deletions

aws_advanced_python_wrapper/aurora_initial_connection_strategy_plugin.py

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616

1717
from enum import Enum
1818
from 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

2122
if TYPE_CHECKING:
2223
from aws_advanced_python_wrapper.driver_dialect import DriverDialect
@@ -29,6 +30,8 @@
2930
from aws_advanced_python_wrapper.host_availability import HostAvailability
3031
from aws_advanced_python_wrapper.hostinfo import HostInfo, HostRole
3132
from aws_advanced_python_wrapper.plugin import Plugin, PluginFactory
33+
from aws_advanced_python_wrapper.utils.accessible_regions import \
34+
parse as parse_accessible_regions
3235
from aws_advanced_python_wrapper.utils.log import Logger
3336
from aws_advanced_python_wrapper.utils.messages import Messages
3437
from 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

506542
class AuroraInitialConnectionStrategyPluginFactory(PluginFactory):
507543
@staticmethod

0 commit comments

Comments
 (0)