Skip to content

Commit 3b4d524

Browse files
authored
test: fix failing integration tests (#1040)
1 parent 11d70fa commit 3b4d524

21 files changed

+198
-95
lines changed

.github/workflows/integration_tests.yml

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,67 @@ permissions:
1111
contents: read # This is required for actions/checkout
1212

1313
jobs:
14-
build-integration-tests:
15-
name: Run Integration Tests
14+
lts-integration-tests:
15+
name: Run LTS Integration Tests
1616
runs-on: ubuntu-latest
1717
strategy:
1818
fail-fast: false
1919
matrix:
2020
python-version: [ "3.8", "3.11" ]
21-
engine-version: [ "lts", "latest"]
21+
environment: [ "mysql", "pg" ]
22+
23+
steps:
24+
- name: 'Clone repository'
25+
uses: actions/checkout@v4
26+
27+
- name: 'Set up JDK 8'
28+
uses: actions/setup-java@v4
29+
with:
30+
distribution: 'corretto'
31+
java-version: 8
32+
33+
- name: Install poetry
34+
shell: bash
35+
run: |
36+
pipx install poetry==1.8.2
37+
poetry config virtualenvs.prefer-active-python true
38+
39+
- name: Install dependencies
40+
run: poetry install
41+
42+
- name: 'Configure AWS Credentials'
43+
uses: aws-actions/configure-aws-credentials@v4
44+
with:
45+
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_DEPLOY_ROLE }}
46+
role-session-name: python_integration_tests
47+
role-duration-seconds: 21600
48+
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
49+
50+
- name: 'Run LTS Integration Tests'
51+
run: |
52+
./gradlew --no-parallel --no-daemon test-python-${{ matrix.python-version }}-${{ matrix.environment }} --info
53+
env:
54+
RDS_CLUSTER_DOMAIN: ${{ secrets.DB_CONN_SUFFIX }}
55+
RDS_DB_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
56+
AURORA_MYSQL_DB_ENGINE_VERSION: lts
57+
AURORA_PG_ENGINE_VERSION: lts
58+
59+
- name: 'Archive LTS results'
60+
if: always()
61+
uses: actions/upload-artifact@v4
62+
with:
63+
name: pytest-integration-report-${{ matrix.python-version }}-${{ matrix.environment }}-lts
64+
path: ./tests/integration/container/reports
65+
retention-days: 5
66+
67+
latest-integration-tests:
68+
name: Run Latest Integration Tests
69+
runs-on: ubuntu-latest
70+
needs: lts-integration-tests
71+
strategy:
72+
fail-fast: false
73+
matrix:
74+
python-version: [ "3.8", "3.11" ]
2275
environment: ["mysql", "pg"]
2376

2477
steps:
@@ -48,19 +101,19 @@ jobs:
48101
role-duration-seconds: 21600
49102
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
50103

51-
- name: 'Run Integration Tests'
104+
- name: 'Run Latest Integration Tests'
52105
run: |
53106
./gradlew --no-parallel --no-daemon test-python-${{ matrix.python-version }}-${{ matrix.environment }} --info
54107
env:
55108
RDS_CLUSTER_DOMAIN: ${{ secrets.DB_CONN_SUFFIX }}
56109
RDS_DB_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
57-
AURORA_MYSQL_DB_ENGINE_VERSION: ${{ matrix.engine-version }}
58-
AURORA_PG_ENGINE_VERSION: ${{ matrix.engine-version }}
110+
AURORA_MYSQL_DB_ENGINE_VERSION: latest
111+
AURORA_PG_ENGINE_VERSION: latest
59112

60-
- name: 'Archive results'
113+
- name: 'Archive Latest results'
61114
if: always()
62115
uses: actions/upload-artifact@v4
63116
with:
64-
name: pytest-integration-report-${{ matrix.python-version }}-${{ matrix.environment }}-${{ matrix.engine-version }}
117+
name: pytest-integration-report-${{ matrix.python-version }}-${{ matrix.environment }}-latest
65118
path: ./tests/integration/container/reports
66119
retention-days: 5

aws_advanced_python_wrapper/driver_dialect.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ class DriverDialect(ABC):
3838
Driver dialects help the driver-agnostic AWS Python Driver interface with the driver-specific functionality of the underlying Python Driver.
3939
"""
4040
_QUERY = "SELECT 1"
41+
_ALL_METHODS = "*"
4142

42-
_executor: ClassVar[Executor] = ThreadPoolExecutor()
43+
_executor: ClassVar[Executor] = ThreadPoolExecutor(thread_name_prefix="DriverDialectExecutor")
4344
_dialect_code: str = DriverDialectCodes.GENERIC
44-
_network_bound_methods: Set[str] = {"*"}
45+
_network_bound_methods: Set[str] = {_ALL_METHODS}
4546
_read_only: bool = False
4647
_autocommit: bool = False
4748
_driver_name: str = "Generic"
@@ -127,7 +128,7 @@ def execute(
127128
*args: Any,
128129
exec_timeout: Optional[float] = None,
129130
**kwargs: Any) -> Cursor:
130-
if method_name not in self._network_bound_methods:
131+
if DriverDialect._ALL_METHODS not in self.network_bound_methods and method_name not in self.network_bound_methods:
131132
return exec_func()
132133

133134
if exec_timeout is None:

aws_advanced_python_wrapper/failover_plugin.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,22 +351,26 @@ def _invalidate_current_connection(self):
351351
"""
352352
conn = self._plugin_service.current_connection
353353
if conn is None:
354-
return
354+
return None
355+
356+
driver_dialect = self._plugin_service.driver_dialect
355357

356358
if self._plugin_service.is_in_transaction:
357359
self._plugin_service.update_in_transaction(True)
358360
try:
361+
driver_dialect.execute("Connection.rollback", lambda: conn.rollback())
359362
conn.rollback()
360363
except Exception:
361364
pass
362365

363-
driver_dialect = self._plugin_service.driver_dialect
364-
if driver_dialect is not None and not driver_dialect.is_closed(conn):
366+
if not driver_dialect.is_closed(conn):
365367
try:
366-
conn.close()
368+
return driver_dialect.execute("Connection.close", lambda: conn.close())
367369
except Exception:
368370
pass
369371

372+
return None
373+
370374
def _invalid_invocation_on_closed_connection(self):
371375
if not self._closed_explicitly:
372376
self._is_closed = False

aws_advanced_python_wrapper/pg_driver_dialect.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class PgDriverDialect(DriverDialect):
4848
"Connection.is_read_only",
4949
"Connection.set_read_only",
5050
"Connection.rollback",
51+
"Connection.close",
5152
"Connection.cursor",
5253
"Cursor.close",
5354
"Cursor.callproc",

aws_advanced_python_wrapper/resources/aws_advanced_python_wrapper_messages.properties

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,6 @@ RoundRobinHostSelector.RoundRobinInvalidHostWeightPairs= [RoundRobinHostSelector
382382
WeightedRandomHostSelector.WeightedRandomInvalidHostWeightPairs= [WeightedRandomHostSelector] The provided host weight pairs have not been configured correctly. Please ensure the provided host weight pairs is a comma separated list of pairs, each pair in the format of <host>:<weight>. Weight values must be an integer greater than or equal to the default weight value of 1. Weight pair: '{}'
383383
WeightedRandomHostSelector.WeightedRandomInvalidDefaultWeight=[WeightedRandomHostSelector] The provided default weight value is not valid. Weight values must be an integer greater than or equal to 1.
384384

385-
SlidingExpirationCache.CleaningUp=[SlidingExpirationCache] Cleaning up...
386-
387385
SqlAlchemyPooledConnectionProvider.PoolNone=[SqlAlchemyPooledConnectionProvider] Attempted to find or create a pool for '{}' but the result of the attempt evaluated to None.
388386
SqlAlchemyPooledConnectionProvider.UnableToCreateDefaultKey=[SqlAlchemyPooledConnectionProvider] Unable to create a default key for internal connection pools. By default, the user parameter is used, but the given user evaluated to None or the empty string (""). Please ensure you have passed a valid user in the connection properties.
389387

aws_advanced_python_wrapper/sqlalchemy_driver_dialect.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def abort_connection(self, conn: Connection):
6767
if isinstance(conn, PoolProxiedConnection):
6868
conn = conn.driver_connection
6969
if conn is None:
70-
return
70+
return None
7171

7272
return self._underlying_driver.abort_connection(conn)
7373

@@ -122,6 +122,6 @@ def transfer_session_state(self, from_conn: Connection, to_conn: Connection):
122122
to_driver_conn = to_conn.driver_connection
123123

124124
if from_driver_conn is None or to_driver_conn is None:
125-
return
125+
return None
126126

127127
return self._underlying_driver.transfer_session_state(from_driver_conn, to_driver_conn)

aws_advanced_python_wrapper/states/session_state.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,6 @@ def copy(self):
9292
new_session_state.readonly = self.readonly.copy()
9393

9494
return new_session_state
95+
96+
def __str__(self):
97+
return f"autocommit: {self.auto_commit}, readonly: {self.readonly}"

aws_advanced_python_wrapper/utils/pg_exception_handler.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@ class PgExceptionHandler(ExceptionHandler):
2727
_PAM_AUTHENTICATION_FAILED_MSG = "PAM authentication failed"
2828
_CONNECTION_FAILED = "connection failed"
2929
_CONSUMING_INPUT_FAILED = "consuming input failed"
30+
_CONNECTION_SOCKET_CLOSED = "connection socket closed"
3031

31-
_NETWORK_ERRORS: List[str]
32-
_ACCESS_ERRORS: List[str]
32+
_NETWORK_ERROR_MESSAGES: List[str] = [
33+
_CONNECTION_FAILED,
34+
_CONSUMING_INPUT_FAILED,
35+
_CONNECTION_SOCKET_CLOSED
36+
]
37+
_ACCESS_ERROR_MESSAGES: List[str] = [
38+
_PASSWORD_AUTHENTICATION_FAILED_MSG,
39+
_PAM_AUTHENTICATION_FAILED_MSG
40+
]
41+
_NETWORK_ERROR_CODES: List[str]
42+
_ACCESS_ERROR_CODES: List[str]
3343

3444
def is_network_exception(self, error: Optional[Exception] = None, sql_state: Optional[str] = None) -> bool:
3545
if isinstance(error, QueryTimeoutError) or isinstance(error, ConnectionTimeout):
@@ -43,15 +53,15 @@ def is_network_exception(self, error: Optional[Exception] = None, sql_state: Opt
4353
# getattr may throw an AttributeError if the error does not have a `sqlstate` attribute
4454
pass
4555

46-
if sql_state is not None and sql_state in self._NETWORK_ERRORS:
56+
if sql_state is not None and sql_state in self._NETWORK_ERROR_CODES:
4757
return True
4858

4959
if isinstance(error, OperationalError):
5060
if len(error.args) == 0:
5161
return False
5262
# Check the error message if this is a generic error
5363
error_msg: str = error.args[0]
54-
return self._CONNECTION_FAILED in error_msg or self._CONSUMING_INPUT_FAILED in error_msg
64+
return any(msg in error_msg for msg in self._NETWORK_ERROR_MESSAGES)
5565

5666
return False
5767

@@ -63,7 +73,7 @@ def is_login_exception(self, error: Optional[Exception] = None, sql_state: Optio
6373
if sql_state is None and hasattr(error, "sqlstate") and error.sqlstate is not None:
6474
sql_state = error.sqlstate
6575

66-
if sql_state is not None and sql_state in self._ACCESS_ERRORS:
76+
if sql_state is not None and sql_state in self._ACCESS_ERROR_CODES:
6777
return True
6878

6979
if isinstance(error, OperationalError):
@@ -72,15 +82,14 @@ def is_login_exception(self, error: Optional[Exception] = None, sql_state: Optio
7282

7383
# Check the error message if this is a generic error
7484
error_msg: str = error.args[0]
75-
if self._PASSWORD_AUTHENTICATION_FAILED_MSG in error_msg \
76-
or self._PAM_AUTHENTICATION_FAILED_MSG in error_msg:
85+
if any(msg in error_msg for msg in self._ACCESS_ERROR_MESSAGES):
7786
return True
7887

7988
return False
8089

8190

8291
class SingleAzPgExceptionHandler(PgExceptionHandler):
83-
_NETWORK_ERRORS: List[str] = [
92+
_NETWORK_ERROR_CODES: List[str] = [
8493
"53", # insufficient resources
8594
"57P01", # admin shutdown
8695
"57P02", # crash shutdown
@@ -92,14 +101,14 @@ class SingleAzPgExceptionHandler(PgExceptionHandler):
92101
"XX" # internal error(backend)
93102
]
94103

95-
_ACCESS_ERRORS: List[str] = [
104+
_ACCESS_ERROR_CODES: List[str] = [
96105
"28000", # PAM authentication errors
97106
"28P01"
98107
]
99108

100109

101110
class MultiAzPgExceptionHandler(PgExceptionHandler):
102-
_NETWORK_ERRORS: List[str] = [
111+
_NETWORK_ERROR_CODES: List[str] = [
103112
"28000", # access denied during reboot, this should be considered a temporary failure
104113
"53", # insufficient resources
105114
"57P01", # admin shutdown
@@ -112,4 +121,4 @@ class MultiAzPgExceptionHandler(PgExceptionHandler):
112121
"XX" # internal error(backend)
113122
]
114123

115-
_ACCESS_ERRORS: List[str] = ["28P01"]
124+
_ACCESS_ERROR_CODES: List[str] = ["28P01"]

aws_advanced_python_wrapper/utils/sliding_expiration_cache.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ def _cleanup_thread_internal(self):
126126
while True:
127127
try:
128128
sleep(self._cleanup_interval_ns / 1_000_000_000)
129-
logger.debug("SlidingExpirationCache.CleaningUp")
130129
self._cleanup_time_ns.set(perf_counter_ns() + self._cleanup_interval_ns)
131130
keys = [key for key, _ in self._cdict.items()]
132131
for key in keys:

aws_advanced_python_wrapper/utils/telemetry/xray_telemetry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def post_copy(context: XRayTelemetryContext, trace_level: TelemetryTraceLevel):
7878
return
7979

8080
if trace_level in [TelemetryTraceLevel.FORCE_TOP_LEVEL, TelemetryTraceLevel.TOP_LEVEL]:
81-
with ThreadPoolExecutor() as executor:
81+
with ThreadPoolExecutor(thread_name_prefix=context.get_name()) as executor:
8282
future = executor.submit(_clone_and_close_context, context, trace_level)
8383
future.result()
8484
else:

0 commit comments

Comments
 (0)