Skip to content

fix: incorrect failover error rewrapping - #1274

Merged
karenc-bq merged 4 commits into
mainfrom
fix/incorrect-failover-error-rewrapping
Aug 25, 2026
Merged

fix: incorrect failover error rewrapping#1274
karenc-bq merged 4 commits into
mainfrom
fix/incorrect-failover-error-rewrapping

Conversation

@karenc-bq

@karenc-bq karenc-bq commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Description

3 fixes: one to how failover errors surface to SQLAlchemy consumers, and two that block upgrading from 3.0.0.

1. Failover errors reach SQLAlchemy unchanged

The dialect mixin caught FailoverSuccessError in do_execute / do_executemany and re-raised pep249.OperationalError. The substitution was unnecessary, and it broke two things.

It was unnecessary because SQLAlchemy classifies by walking orig.__class__.__mro__ and matching bases by class name against sqlalchemy.exc. FailoverError already derives from the wrapper's pep249.OperationalError, so the family already mapped to sqlalchemy.exc.OperationalError without any rewrap. Measured both ways: same sqlalchemy.exc class, differing only in .orig.

What it broke:

  1. DBAPIError.orig became the substitute, so isinstance(err.orig, FailoverSuccessError) stopped matching. 3.0.0's shipped example used that check.
  2. is_disconnect no longer received a FailoverError, so it delegated to MySQLDialect_mysqlconnector.is_disconnect, which reads e.errno. The wrapper's PEP-249 errors have no errno, and SQLAlchemy calls is_disconnect at the top of Connection._handle_dbapi_exception outside any try, so the resulting AttributeError escaped and the application's except DBAPIError never ran.

Reproduced on a real Aurora MySQL failover: a successful failover surfaced to the consumer as AttributeError: 'OperationalError' object has no attribute 'errno'. The connection was usable; only the signal telling the application to retry was lost. docs/examples/MySQLSQLAlchemyFailover.py catches OperationalError, which that bypasses.

The rewrap also made is_disconnect unreachable on the do_execute path, where it is needed. Removing it restores the override that keeps a recoverable failover from invalidating the pooled connection.

This change also defaults errno and sqlstate to None on pep249.Error. Because dialect.loaded_dbapi is this package, SQLAlchemy's MySQL dialect probes e.errno on anything that is an OperationalError or InterfaceError. Nine wrapper errors qualify: the FailoverError family plus QueryTimeoutError, AwsConnectError and ReadWriteSplittingError. Each was one unguarded probe away from the same AttributeError.

2. Type stubs are no longer runtime dependencies

boto3-stubs and types_aws_xray_sdk were declared in [tool.poetry.dependencies], which made upgrading impossible for some consumers.

3. The pre-3.1.0 dialect import path resolves again

SqlAlchemyOrmMysqlDialect moved to sqlalchemy_dialects/mysql.py::AwsWrapperMySQLConnectorDialect without an alias, so code that imported the class directly broke with ModuleNotFoundError. URL-based configuration was unaffected: the driver name aws_wrapper_mysqlconnector did not change. This adds a shim for the direct-import case, with a DeprecationWarning.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@karenc-bq
karenc-bq force-pushed the fix/incorrect-failover-error-rewrapping branch from 2372028 to bedffbf Compare August 24, 2026 23:35
Comment thread aws_advanced_python_wrapper/sqlalchemy/mysql_orm_dialect.py Outdated
@karenc-bq
karenc-bq merged commit c971ee2 into main Aug 25, 2026
9 checks passed
@karenc-bq
karenc-bq deleted the fix/incorrect-failover-error-rewrapping branch August 25, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants