From f7ba2664ddb428ff9fbc0b9ae6074e71fbf98308 Mon Sep 17 00:00:00 2001 From: Sophia Chu Date: Mon, 1 Dec 2025 15:56:20 -0800 Subject: [PATCH 1/3] docs: srw --- docs/README.md | 1 + docs/examples/MySQLReadWriteSplitting.py | 2 +- .../examples/MySQLSimpleReadWriteSplitting.py | 105 +++++++++++++++++ docs/examples/PGReadWriteSplitting.py | 2 +- docs/examples/PGSimpleReadWriteSplitting.py | 107 +++++++++++++++++ .../UsingThePythonDriver.md | 27 ++--- .../UsingTheSimpleReadWriteSplittingPlugin.md | 108 ++++++++++++++++++ 7 files changed, 337 insertions(+), 15 deletions(-) create mode 100644 docs/examples/MySQLSimpleReadWriteSplitting.py create mode 100644 docs/examples/PGSimpleReadWriteSplitting.py create mode 100644 docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md diff --git a/docs/README.md b/docs/README.md index ef76cae1..24f9eeec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,7 @@ - [Federated Authentication Plugin](./using-the-python-driver/using-plugins/UsingTheFederatedAuthenticationPlugin.md) - [Read Write Splitting Plugin](./using-the-python-driver/using-plugins/UsingTheReadWriteSplittingPlugin.md) - [Reader Selection Strategies](./using-the-python-driver/ReaderSelectionStrategies.md) + - [Simple Read Write Splitting Plugin](./using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md) - [Fastest Response Strategy Plugin](./using-the-python-driver/using-plugins/UsingTheFastestResponseStrategyPlugin.md) - [Host Availability Strategy](./using-the-python-driver/HostAvailabilityStrategy.md) - [Development Guide](./development-guide/DevelopmentGuide.md) diff --git a/docs/examples/MySQLReadWriteSplitting.py b/docs/examples/MySQLReadWriteSplitting.py index b1d6717e..84e3459e 100644 --- a/docs/examples/MySQLReadWriteSplitting.py +++ b/docs/examples/MySQLReadWriteSplitting.py @@ -20,7 +20,7 @@ from aws_advanced_python_wrapper.hostinfo import HostInfo from aws_advanced_python_wrapper.pep249 import Connection -import mysql.connector +import mysql.connector # type: ignore from aws_advanced_python_wrapper import AwsWrapperConnection from aws_advanced_python_wrapper.connection_provider import \ diff --git a/docs/examples/MySQLSimpleReadWriteSplitting.py b/docs/examples/MySQLSimpleReadWriteSplitting.py new file mode 100644 index 00000000..9db6868f --- /dev/null +++ b/docs/examples/MySQLSimpleReadWriteSplitting.py @@ -0,0 +1,105 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union + +if TYPE_CHECKING: + from aws_advanced_python_wrapper.pep249 import Connection + +import mysql.connector # type: ignore + +from aws_advanced_python_wrapper import AwsWrapperConnection +from aws_advanced_python_wrapper.errors import ( + FailoverFailedError, FailoverSuccessError, + TransactionResolutionUnknownError) + +def configure_initial_session_states(conn: Connection): + awscursor = conn.cursor() + awscursor.execute("SET time_zone = 'UTC'") + + +def execute_queries_with_failover_handling(conn: Connection, sql: str, params: Optional[Union[Dict, Tuple]] = None): + try: + cursor = conn.cursor() + cursor.execute(sql, params) + return cursor + + except FailoverSuccessError: + # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. + # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + + # The old cursor is no longer reusable and the application needs to reconfigure sessions states. + configure_initial_session_states(conn) + # Retry query + cursor = conn.cursor() + cursor.execute(sql) + return cursor + + except FailoverFailedError as e: + # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: + # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + raise e + + except TransactionResolutionUnknownError as e: + # User application should check the status of the failed transaction and restart it if needed. See: + # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + raise e + + +if __name__ == "__main__": + params = { + "host": "database.cluster-xyz.us-east-1.rds.amazonaws.com", + "database": "mysql", + "user": "admin", + "password": "pwd", + "plugins": "srw,failover", + "srw_write_endpoint": "database.cluster-xyz.us-east-1.rds.amazonaws.com", # Replace with write endpoint + "srw_read_endpoint": "database.cluster-ro-xyz.us-east-1.rds.amazonaws.com", # Replace with read endpoint + "srw_verify_new_connections": "True", # Enables role-verification for new endpoints + "wrapper_dialect": "aurora-mysql", + "autocommit": True + } + + """ Setup step: open connection and create tables """ + with AwsWrapperConnection.connect(mysql.connector.Connect, **params) as conn: + configure_initial_session_states(conn) + execute_queries_with_failover_handling( + conn, "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)") + execute_queries_with_failover_handling( + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200)) + execute_queries_with_failover_handling( + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200)) + + """ Example step: open connection and perform transaction """ + try: + with AwsWrapperConnection.connect(mysql.connector.Connect, **params) as conn, conn.cursor() as cursor: + configure_initial_session_states(conn) + + execute_queries_with_failover_handling( + conn, "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", ("Jane Doe",)) + execute_queries_with_failover_handling( + conn, "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", ("John Smith",)) + + # Internally switch to a reader connection + conn.read_only = True + for i in range(2): + cursor = execute_queries_with_failover_handling(conn, "SELECT * FROM bank_test WHERE id = %s", (i,)) + for record in cursor: + print(record) + + finally: + with AwsWrapperConnection.connect(mysql.connector.Connect, **params) as conn: + execute_queries_with_failover_handling(conn, "DROP TABLE bank_test") diff --git a/docs/examples/PGReadWriteSplitting.py b/docs/examples/PGReadWriteSplitting.py index baeeb52c..6e0a44c6 100644 --- a/docs/examples/PGReadWriteSplitting.py +++ b/docs/examples/PGReadWriteSplitting.py @@ -20,7 +20,7 @@ from aws_advanced_python_wrapper.hostinfo import HostInfo from aws_advanced_python_wrapper.pep249 import Connection -import psycopg +import psycopg # type: ignore from aws_advanced_python_wrapper import AwsWrapperConnection from aws_advanced_python_wrapper.connection_provider import \ diff --git a/docs/examples/PGSimpleReadWriteSplitting.py b/docs/examples/PGSimpleReadWriteSplitting.py new file mode 100644 index 00000000..216f69a0 --- /dev/null +++ b/docs/examples/PGSimpleReadWriteSplitting.py @@ -0,0 +1,107 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union + +if TYPE_CHECKING: + from aws_advanced_python_wrapper.pep249 import Connection + +import psycopg # type: ignore + +from aws_advanced_python_wrapper import AwsWrapperConnection +from aws_advanced_python_wrapper.errors import ( + FailoverFailedError, FailoverSuccessError, + TransactionResolutionUnknownError) +from aws_advanced_python_wrapper.host_monitoring_plugin import MonitoringThreadContainer + +def configure_initial_session_states(conn: Connection): + awscursor = conn.cursor() + awscursor.execute("SET TIME ZONE 'UTC'") + + +def execute_queries_with_failover_handling(conn: Connection, sql: str, params: Optional[Union[Dict, Tuple]] = None): + try: + cursor = conn.cursor() + cursor.execute(sql, params) + return cursor + + except FailoverSuccessError: + # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. + # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + + # The old cursor is no longer reusable and the application needs to reconfigure sessions states. + configure_initial_session_states(conn) + # Retry query + cursor = conn.cursor() + cursor.execute(sql) + return cursor + + except FailoverFailedError as e: + # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: + # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + raise e + + except TransactionResolutionUnknownError as e: + # User application should check the status of the failed transaction and restart it if needed. See: + # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + raise e + + +if __name__ == "__main__": + params = { + "host": "database.cluster-xyz.us-east-1.rds.amazonaws.com", + "dbname": "postgres", + "user": "john", + "password": "pwd", + "plugins": "srw,failover", + "srw_write_endpoint": "database.cluster-xyz.us-east-1.rds.amazonaws.com", # Replace with write endpoint + "srw_read_endpoint": "database.cluster-ro-xyz.us-east-1.rds.amazonaws.com", # Replace with read endpoint + "srw_verify_new_connections": "True", # Enables role-verification for new endpoints + "wrapper_dialect": "aurora-pg", + "autocommit": True + } + + """ Setup step: open connection and create tables """ + with AwsWrapperConnection.connect(psycopg.Connection.connect, **params) as conn: + configure_initial_session_states(conn) + execute_queries_with_failover_handling( + conn, "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)") + execute_queries_with_failover_handling( + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200)) + execute_queries_with_failover_handling( + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200)) + + """ Example step: open connection and perform transaction """ + try: + with AwsWrapperConnection.connect(psycopg.Connection.connect, **params) as conn, conn.cursor() as cursor: + configure_initial_session_states(conn) + + execute_queries_with_failover_handling( + conn, "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", ("Jane Doe",)) + execute_queries_with_failover_handling( + conn, "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", ("John Smith",)) + + # Internally switch to a reader connection + conn.read_only = True + for i in range(2): + cursor = execute_queries_with_failover_handling(conn, "SELECT * FROM bank_test WHERE id = %s", (i,)) + results = cursor.fetchall() + for record in results: + print(record) + + finally: + with AwsWrapperConnection.connect(psycopg.Connection.connect, **params) as conn: + execute_queries_with_failover_handling(conn, "DROP TABLE bank_test") diff --git a/docs/using-the-python-driver/UsingThePythonDriver.md b/docs/using-the-python-driver/UsingThePythonDriver.md index c33e4e95..9bb19cd6 100644 --- a/docs/using-the-python-driver/UsingThePythonDriver.md +++ b/docs/using-the-python-driver/UsingThePythonDriver.md @@ -61,19 +61,20 @@ Plugins are loaded and managed through the Connection Plugin Manager and may be The AWS Advanced Python Driver has several built-in plugins that are available to use. Please visit the individual plugin page for more details. -| Plugin name | Plugin Code | Database Compatibility | Description | Additional Required Dependencies | -|--------------------------------------------------------------------------------------------------------|-----------------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| -| [Failover Connection Plugin](./using-plugins/UsingTheFailoverPlugin.md) | `failover` | Aurora | Enables the failover functionality supported by Amazon Aurora clusters. Prevents opening a wrong connection to an old writer host dues to stale DNS after failover event. This plugin is enabled by default. | None | -| [Host Monitoring Connection Plugin](./using-plugins/UsingTheHostMonitoringPlugin.md) | `host_monitoring_v2` or `host_monitoring` | Aurora | Enables enhanced host connection failure monitoring, allowing faster failure detection rates. This plugin is enabled by default. | None | -| [IAM Authentication Connection Plugin](./using-plugins/UsingTheIamAuthenticationPlugin.md) | `iam` | Any database | Enables users to connect to their Amazon Aurora clusters using AWS Identity and Access Management (IAM). | [Boto3 - AWS SDK for Python](https://aws.amazon.com/sdk-for-python/) | -| [AWS Secrets Manager Connection Plugin](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | `aws_secrets_manager` | Any database | Enables fetching database credentials from the AWS Secrets Manager service. | [Boto3 - AWS SDK for Python](https://aws.amazon.com/sdk-for-python/) | -| [Federated Authentication Connection Plugin](./using-plugins/UsingTheFederatedAuthenticationPlugin.md) | `federated_auth` | Any database | Enables users to authenticate via Federated Identity and then database access via IAM. | [Boto3 - AWS SDK for Python](https://aws.amazon.com/sdk-for-python/) | -| Aurora Stale DNS Plugin | `stale_dns` | Aurora | Prevents incorrectly opening a new connection to an old writer host when DNS records have not yet updated after a recent failover event.

:warning:**Note:** Contrary to `failover` plugin, `stale_dns` plugin doesn't implement failover support itself. It helps to eliminate opening wrong connections to an old writer host after cluster failover is completed.

:warning:**Note:** This logic is already included in `failover` plugin so you can omit using both plugins at the same time. | None | -| [Aurora Connection Tracker Plugin](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | `aurora_connection_tracker` | Aurora | Tracks all the opened connections. In the event of a cluster failover, the plugin will close all the impacted connections to the host. This plugin is enabled by default. | None | -| [Read Write Splitting Plugin](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | `read_write_splitting` | Aurora | Enables read write splitting functionality where users can switch between database reader and writer instances. | None | -| [Fastest Response Strategy Plugin](./using-plugins/UsingTheFastestResponseStrategyPlugin.md) | `fastest_response_strategy` | Aurora | A host selection strategy plugin that uses a host monitoring service to monitor each reader host's response time and choose the host with the fastest response. | None | -| [Blue/Green Deployment Plugin](./using-plugins/UsingTheBlueGreenPlugin.md) | `bg` | Aurora,
RDS Instance | Enables client-side Blue/Green Deployment support. | None | -| [Limitless Plugin](using-plugins/UsingTheLimitlessPlugin.md) | `limitless` | Aurora | Enables client-side load-balancing of Transaction Routers on Amazon Aurora Limitless Databases. | None | +| Plugin name | Plugin Code | Database Compatibility | Description | Additional Required Dependencies | +|--------------------------------------------------------------------------------------------------------|-------------------------------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| +| [Failover Connection Plugin](./using-plugins/UsingTheFailoverPlugin.md) | `failover` | Aurora | Enables the failover functionality supported by Amazon Aurora clusters. Prevents opening a wrong connection to an old writer host dues to stale DNS after failover event. This plugin is enabled by default. | None | +| [Host Monitoring Connection Plugin](./using-plugins/UsingTheHostMonitoringPlugin.md) | `host_monitoring_v2` or `host_monitoring` | Aurora | Enables enhanced host connection failure monitoring, allowing faster failure detection rates. This plugin is enabled by default. | None | +| [IAM Authentication Connection Plugin](./using-plugins/UsingTheIamAuthenticationPlugin.md) | `iam` | Any database | Enables users to connect to their Amazon Aurora clusters using AWS Identity and Access Management (IAM). | [Boto3 - AWS SDK for Python](https://aws.amazon.com/sdk-for-python/) | +| [AWS Secrets Manager Connection Plugin](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | `aws_secrets_manager` | Any database | Enables fetching database credentials from the AWS Secrets Manager service. | [Boto3 - AWS SDK for Python](https://aws.amazon.com/sdk-for-python/) | +| [Federated Authentication Connection Plugin](./using-plugins/UsingTheFederatedAuthenticationPlugin.md) | `federated_auth` | Any database | Enables users to authenticate via Federated Identity and then database access via IAM. | [Boto3 - AWS SDK for Python](https://aws.amazon.com/sdk-for-python/) | +| Aurora Stale DNS Plugin | `stale_dns` | Aurora | Prevents incorrectly opening a new connection to an old writer host when DNS records have not yet updated after a recent failover event.

:warning:**Note:** Contrary to `failover` plugin, `stale_dns` plugin doesn't implement failover support itself. It helps to eliminate opening wrong connections to an old writer host after cluster failover is completed.

:warning:**Note:** This logic is already included in `failover` plugin so you can omit using both plugins at the same time. | None | +| [Aurora Connection Tracker Plugin](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | `aurora_connection_tracker` | Aurora | Tracks all the opened connections. In the event of a cluster failover, the plugin will close all the impacted connections to the host. This plugin is enabled by default. | None | +| [Read Write Splitting Plugin](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | `read_write_splitting` | Aurora | Enables read write splitting functionality where users can switch between database reader and writer instances. | None | +| [Simple Read Write Splitting Plugin](./using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md) | `srw` | Any database | Enables read write splitting functionality where users can switch between reader and writer endpoints. | None | +| [Fastest Response Strategy Plugin](./using-plugins/UsingTheFastestResponseStrategyPlugin.md) | `fastest_response_strategy` | Aurora | A host selection strategy plugin that uses a host monitoring service to monitor each reader host's response time and choose the host with the fastest response. | None | +| [Blue/Green Deployment Plugin](./using-plugins/UsingTheBlueGreenPlugin.md) | `bg` | Aurora,
RDS Instance | Enables client-side Blue/Green Deployment support. | None | +| [Limitless Plugin](using-plugins/UsingTheLimitlessPlugin.md) | `limitless` | Aurora | Enables client-side load-balancing of Transaction Routers on Amazon Aurora Limitless Databases. | None | In addition to the built-in plugins, you can also create custom plugins more suitable for your needs. For more information, see [Custom Plugins](../development-guide/LoadablePlugins.md#using-custom-plugins). diff --git a/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md b/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md new file mode 100644 index 00000000..76c7893b --- /dev/null +++ b/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md @@ -0,0 +1,108 @@ +# Simple Read/Write Splitting Plugin + +The Simple Read/Write Splitting Plugin adds functionality to switch between endpoints by setting the `read_only` attribute of an `AwsWrapperConnection`. Based on the values provided in the properties, upon setting `read_only` to `True`, the plugin will connect to the specified endpoint for read operations. When setting `read_only` to `False`, the plugin will connect to the specified endpoint for write operations. Future `read_only` settings will switch between the established writer and reader connections according to the `read_only` setting. + +The plugin will use the current connection, which may be the writer or initial connection, as a fallback if the reader connection is unable to be established. This includes when `srw_verify_new_connections` is set to True and no *verified* reader connection is able to be established. + +The plugin does not rely on cluster topology. It relies purely on the provided endpoints and their DNS resolution. + +## Loading the Simple Read/Write Splitting Plugin + +The Simple Read/Write Splitting Plugin is not loaded by default. To load the plugin, include `srw` in the [`plugins`](../UsingThePythonDriver.md#connection-plugin-manager-parameters) connection parameter: + +```python +params = { + "plugins": "srw,failover,host_monitoring_v2", + # Add other connection properties below... +} + +# If using MySQL: +conn = AwsWrapperConnection.connect(mysql.connector.connect, **params) + +# If using Postgres: +conn = AwsWrapperConnection.connect(psycopg.Connection.connect, **params) +``` + +## Simple Read/Write Splitting Plugin Parameters +| Parameter | Value | Required | Description | Default Value | Example Values | +|--------------------------------------|:-------:|:--------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------------| +| `srw_write_endpoint` | String | Yes | The endpoint to connect to upon setting `read_only` to `False`. | `None` | `.cluster-..rds.amazonaws.com` | +| `srw_read_endpoint` | String | Yes | The endpoint to connect to upon setting `read_only` to `True`. | `None` | `.cluster-ro-..rds.amazonaws.com` | +| `srw_verify_new_connections` | Boolean | No | Enables writer/reader verification for new connections made by the Simple Read/Write Splitting Plugin. More details below. | `True` | `True`, `False` | +| `srw_verify_initial_connection_type` | String | No | If `srw_verify_new_connections` is set to `True`, this parameter will verify the initial opened connection to be either a writer or a reader. More details below. | `None` | `writer`, `reader` | +| `srw_connect_retry_timeout_ms` | Integer | No | If `srw_verify_new_connections` is set to `True`, this parameter sets the maximum allowed time in milliseconds for retrying connection attempts. More details below. | `6000` | `60000` | +| `srw_connect_retry_interval_ms` | Integer | No | If `srw_verify_new_connections` is set to `True`, this parameter sets the time delay in milliseconds between each retry of opening a connection. More details below. | `100` | `1000` | + +## How the Simple Read/Write Splitting Plugin Verifies Connections + +The property `srw_verify_new_connections` is enabled by default. This means that when new connections are made with the Simple Read/Write Splitting Plugin, a query is sent to the new connection to verify its role. If the connection cannot be verified as having the correct role, ie. a write connection is not connected to a writer, or a read connection is not connected to a reader, the plugin will retry the connection up to the time limit of `srw_connect_retry_timeout_ms`. + +The values of `srw_connect_retry_timeout_ms` and `srw_connect_retry_interval_ms` control the timing and aggressiveness of the plugin's retries. + +Additionally, to consistently ensure the role of connections made with the plugin, the plugin also provides role verification for the initial connection. When connecting with an RDS writer cluster or reader cluster endpoint, the plugin will retry the initial connection up to `srw_connect_retry_timeout_ms` until it has verified the intended role of the endpoint. +If it is unable to return a verified initial connection, it will log a message and continue with the normal workflow of the other plugins. +When connecting with custom endpoints and other non-standard URLs, role verification on the initial connection can also be triggered by providing the expected role through the `srw_verify_initial_connection_type` parameter. Set this to `writer` or `reader` accordingly. + +## Limitations When Verifying Connections + +#### Non-RDS clusters +The verification step determines the role of the connection by executing a query against it. If the endpoint is not part of an Aurora or RDS cluster, the plugin will not be able to verify the role, so `srw_verify_new_connections` *must* be set to `False`. + +#### Autocommit +The verification logic results in errors such as `Cannot change transaction read-only property in the middle of a transaction` from the underlying driver when: +- autocommit is set to false +- setReadOnly is called +- as part of setReadOnly, a new connection is opened +- that connection's role is verified + +This is a result of the plugin executing the role-verification query against a new connection, and when autocommit is false, this opens a transaction. + +If autocommit is essential to a workflow, either ensure the plugin has connected to the desired target connection of the setReadOnly query before setting autocommit to false or disable `srw_verify_new_connections`. + +## Using the Simple Read/Write Splitting Plugin with RDS Proxy + +RDS Proxy provides connection pooling and management that significantly improves application scalability by reducing database connection overhead and enabling thousands of concurrent connections through +connection multiplexing. Connecting exclusively through the proxy endpoint ensures consistent connection management, automatic failover handling, and centralized monitoring, while protecting the underlying database from connection exhaustion +and providing a stable abstraction layer that remains consistent even when database topology changes. To take full advantage of the benefits of RDS Proxy, it is recommended to only connect through RDS Proxy endpoints. By providing the read/write endpoint and a read-only endpoint to the Simple Read/Write Splitting Plugin, the AWS Advanced Python Driver will connect using +these endpoints any time setReadOnly is called. + +There are limitations with the AWS Advanced Python Driver and RDS Proxy. This is currently intended, by design, since RDS Proxy decides which database instance is used based on many criteria (on a per-request basis). Due to this, functionality like [Failover](./UsingTheFailoverPlugin.md), [Enhanced Failure Monitoring](./UsingTheHostMonitoringPlugin.md), and topology-based [Read/Write Splitting](./UsingTheReadWriteSplittingPlugin.md) is not compatible since the driver relies on cluster topology and RDS Proxy handles this automatically. However, the driver can still be used to handle authentication workflows and to perform the endpoint-based read/write splitting of the Simple Read/Write Splitting Plugin. + +## Using the Simple Read/Write Splitting Plugin against non-RDS clusters + +The Simple Read/Write Splitting Plugin can be used to switch between any two endpoints. If the endpoints do not direct to an RDS cluster, ensure the property `srw_verify_new_connections` is set to `False`. See [Limitations of srw_verify_new_connections](UsingTheSimpleReadWriteSplittingPlugin.md#non-rds-clusters) for details. + +## Limitations + +### General plugin limitations + +When a `Cursor` is created, it is bound to the database connection established at that moment. Consequently, if the Simple Read/Write Splitting Plugin switches the internal connection, any `Cursor` objects created before this will continue using the old database connection. This bypasses the desired functionality provided by the plugin. To prevent these scenarios, an exception will be thrown if your code uses any `Cursor` objects created before a change in internal connection. To solve this problem, please ensure you create new `Cursor` objects after switching between the writer/reader. + +### Failover + +Immediately following a failover event, due to DNS caching, an RDS cluster endpoint may connect to the previous writer, and the read-only endpoint may connect to the new writer instance. + +To avoid stale DNS connections, enable `srw_verify_new_connections`, as this will retry the connection until the role has been verified. Service for Aurora clusters is typically restored in less than 60 seconds, and often less than 30 seconds. RDS Proxy endpoints to Aurora databases can update in as little as 3 seconds. Depending on your configuration and cluster availability `srw_connect_retry_timeout_ms` and `srw_connect_retry_interval_ms` may be set to customize the timing of the retries. + +Following failover, endpoints that point to specific instances will be impacted if their target instance was demoted to a reader or promoted to a writer. The Simple Read/Write Splitting Plugin always connects to the endpoint provided in the initial connection properties when the `read_only` attribute is set. We suggest using endpoints that return connections with a specific role such as cluster or read-only endpoints, or using the [Read/Write Splitting Plugin](UsingTheReadWriteSplittingPlugin.md) to connect to instances based on the cluster's current topology. + +### Session state + +There are many session state attributes that can change during a session, and many ways to change them. Consequently, the Simple Read/Write Splitting Plugin has limited support for transferring session state between connections. The following attributes will be automatically transferred when switching connections: + +For Postgres: +- read_only +- autocommit +- transaction isolation level + +For MySQL: +- autocommit + +All other session state attributes will be lost when switching connections between the writer/reader. + +If your SQL workflow depends on session state attributes that are not mentioned above, you will need to re-configure those attributes each time that you switch between the writer/reader. + +### Sample Code +[Postgres simple read/write splitting sample code](../../examples/PGSimpleReadWriteSplitting.py) +[MySQL simple read/write splitting sample code](../../examples/MySQLSimpleReadWriteSplitting.py) + From 91e2f8c4578e647e9d1267d62d7a352f701774c1 Mon Sep 17 00:00:00 2001 From: Sophia Chu Date: Tue, 2 Dec 2025 15:42:56 -0800 Subject: [PATCH 2/3] fix: use aws in links --- Maintenance.md | 16 ++++---- README.md | 6 +-- benchmarks/README.md | 2 +- docs/development-guide/DevelopmentGuide.md | 2 +- docs/examples/MySQLFailover.py | 6 +-- docs/examples/MySQLReadWriteSplitting.py | 32 ++++++++++----- .../examples/MySQLSimpleReadWriteSplitting.py | 38 ++++++++++++------ docs/examples/PGReadWriteSplitting.py | 32 ++++++++++----- docs/examples/PGSimpleReadWriteSplitting.py | 40 ++++++++++++------- 9 files changed, 111 insertions(+), 63 deletions(-) diff --git a/Maintenance.md b/Maintenance.md index da59471c..268afe05 100644 --- a/Maintenance.md +++ b/Maintenance.md @@ -1,13 +1,13 @@ # Release Schedule -| Release Date | Release | -|-------------------|--------------------------------------------------------------------------------------------| -| May 16, 2024 | [Release 1.0.0](https://github.com/awslabs/aws-advanced-python-wrapper/releases/tag/1.0.0) | -| July 31, 2024 | [Release 1.1.0](https://github.com/awslabs/aws-advanced-python-wrapper/releases/tag/1.1.0) | -| October 18, 2024 | [Release 1.1.1](https://github.com/awslabs/aws-advanced-python-wrapper/releases/tag/1.1.1) | -| December 12, 2024 | [Release 1.2.0](https://github.com/awslabs/aws-advanced-python-wrapper/releases/tag/1.2.0) | -| July 28, 2025 | [Release 1.3.0](https://github.com/awslabs/aws-advanced-python-wrapper/releases/tag/1.3.0) | -| October 17, 2025 | [Release 1.4.0](https://github.com/awslabs/aws-advanced-python-wrapper/releases/tag/1.4.0) | +| Release Date | Release | +|-------------------|----------------------------------------------------------------------------------------| +| May 16, 2024 | [Release 1.0.0](https://github.com/aws/aws-advanced-python-wrapper/releases/tag/1.0.0) | +| July 31, 2024 | [Release 1.1.0](https://github.com/aws/aws-advanced-python-wrapper/releases/tag/1.1.0) | +| October 18, 2024 | [Release 1.1.1](https://github.com/aws/aws-advanced-python-wrapper/releases/tag/1.1.1) | +| December 12, 2024 | [Release 1.2.0](https://github.com/aws/aws-advanced-python-wrapper/releases/tag/1.2.0) | +| July 28, 2025 | [Release 1.3.0](https://github.com/aws/aws-advanced-python-wrapper/releases/tag/1.3.0) | +| October 17, 2025 | [Release 1.4.0](https://github.com/aws/aws-advanced-python-wrapper/releases/tag/1.4.0) | `aws-advanced-python-wrapper` [follows semver](https://semver.org/#semantic-versioning-200) which means we will only release breaking changes in major versions. Generally speaking patches will be released to fix existing problems without diff --git a/README.md b/README.md index 89615730..e259fe22 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Enhanced Failure Monitoring (EFM) is a feature available from the [Host Monitori The AWS Advanced Python Driver also works with RDS provided databases that are not Aurora. -Please visit [this page](./docs/using-the-python-driver/UsingThePythonDriver.md#using-the-aws-python-driver-with-plain-rds-databases) for more information. +Please visit [this page](./docs/using-the-python-driver/UsingThePythonDriver.md#using-the-aws-advanced-python-driver-with-plain-rds-databases) for more information. ## Getting Started @@ -195,11 +195,11 @@ Psycopg and MySQL Connector/Python do not provide client-side query timeouts. Wh ## Getting Help and Opening Issues If you encounter a bug with the AWS Advanced Python Driver, we would like to hear about it. -Please search the [existing issues](https://github.com/awslabs/aws-advanced-python-wrapper/issues) to see if others are also experiencing the issue before reporting the problem in a new issue. GitHub issues are intended for bug reports and feature requests. +Please search the [existing issues](https://github.com/aws/aws-advanced-python-wrapper/issues) to see if others are also experiencing the issue before reporting the problem in a new issue. GitHub issues are intended for bug reports and feature requests. When opening a new issue, please fill in all required fields in the issue template to help expedite the investigation process. -For all other questions, please use [GitHub discussions](https://github.com/awslabs/aws-advanced-python-wrapper/discussions). +For all other questions, please use [GitHub discussions](https://github.com/aws/aws-advanced-python-wrapper/discussions). ## How to Contribute diff --git a/benchmarks/README.md b/benchmarks/README.md index dd019697..b194e571 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -11,7 +11,7 @@ The benchmarks do not measure the performance of target Python drivers nor the p ## Benchmark Results -Tests ran on commit [3d473f6c12f495762d3b7436a97a0e6bd76361d7](https://github.com/awslabs/aws-advanced-python-wrapper/commit/3d473f6c12f495762d3b7436a97a0e6bd76361d7) +Tests ran on commit [3d473f6c12f495762d3b7436a97a0e6bd76361d7](https://github.com/aws/aws-advanced-python-wrapper/commit/3d473f6c12f495762d3b7436a97a0e6bd76361d7) ## Summary | Benchmark | Overhead | diff --git a/docs/development-guide/DevelopmentGuide.md b/docs/development-guide/DevelopmentGuide.md index 391b3237..36253f48 100644 --- a/docs/development-guide/DevelopmentGuide.md +++ b/docs/development-guide/DevelopmentGuide.md @@ -6,7 +6,7 @@ Make sure you have Python 3.8 - 3.11 (inclusive) installed, along with your choi Clone the AWS Advanced Python Driver repository: ```bash -git clone https://github.com/awslabs/aws-advanced-python-wrapper.git +git clone https://github.com/aws/aws-advanced-python-wrapper.git ``` You can now make changes in the repository. diff --git a/docs/examples/MySQLFailover.py b/docs/examples/MySQLFailover.py index 2444b341..b7e3b67f 100644 --- a/docs/examples/MySQLFailover.py +++ b/docs/examples/MySQLFailover.py @@ -40,7 +40,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverSuccessError: # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoversuccesserror # The old cursor is no longer reusable and the application needs to reconfigure sessions states. configure_initial_session_states(conn) @@ -51,12 +51,12 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverFailedError as e: # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoverfailederror raise e except TransactionResolutionUnknownError as e: # User application should check the status of the failed transaction and restart it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#transactionresolutionunknownerror raise e diff --git a/docs/examples/MySQLReadWriteSplitting.py b/docs/examples/MySQLReadWriteSplitting.py index 84e3459e..8672ec82 100644 --- a/docs/examples/MySQLReadWriteSplitting.py +++ b/docs/examples/MySQLReadWriteSplitting.py @@ -58,7 +58,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverSuccessError: # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoversuccesserror # The old cursor is no longer reusable and the application needs to reconfigure sessions states. configure_initial_session_states(conn) @@ -69,12 +69,12 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverFailedError as e: # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoverfailederror raise e except TransactionResolutionUnknownError as e: # User application should check the status of the failed transaction and restart it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#transactionresolutionunknownerror raise e @@ -86,7 +86,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O "password": "pwd", "plugins": "read_write_splitting,failover", "wrapper_dialect": "aurora-mysql", - "autocommit": True + "autocommit": True, } """ @@ -101,11 +101,15 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O with AwsWrapperConnection.connect(mysql.connector.Connect, **params) as conn: configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)") + conn, + "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)", + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200) + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200) + ) """ Example step: open connection and perform transaction """ try: @@ -113,14 +117,22 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", ("Jane Doe",)) + conn, + "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", + ("Jane Doe",), + ) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", ("John Smith",)) + conn, + "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", + ("John Smith",), + ) # Internally switch to a reader connection conn.read_only = True for i in range(2): - cursor = execute_queries_with_failover_handling(conn, "SELECT * FROM bank_test WHERE id = %s", (i,)) + cursor = execute_queries_with_failover_handling( + conn, "SELECT * FROM bank_test WHERE id = %s", (i,) + ) for record in cursor: print(record) diff --git a/docs/examples/MySQLSimpleReadWriteSplitting.py b/docs/examples/MySQLSimpleReadWriteSplitting.py index 9db6868f..6e6b1f2e 100644 --- a/docs/examples/MySQLSimpleReadWriteSplitting.py +++ b/docs/examples/MySQLSimpleReadWriteSplitting.py @@ -39,7 +39,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverSuccessError: # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoversuccesserror # The old cursor is no longer reusable and the application needs to reconfigure sessions states. configure_initial_session_states(conn) @@ -50,38 +50,42 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverFailedError as e: # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoverfailederror raise e except TransactionResolutionUnknownError as e: # User application should check the status of the failed transaction and restart it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#transactionresolutionunknownerror raise e if __name__ == "__main__": params = { - "host": "database.cluster-xyz.us-east-1.rds.amazonaws.com", + "host": "rds-proxy-name.proxy-xyz.us-east-1.rds.amazonaws.com", "database": "mysql", "user": "admin", "password": "pwd", "plugins": "srw,failover", - "srw_write_endpoint": "database.cluster-xyz.us-east-1.rds.amazonaws.com", # Replace with write endpoint - "srw_read_endpoint": "database.cluster-ro-xyz.us-east-1.rds.amazonaws.com", # Replace with read endpoint + "srw_write_endpoint": "rds-proxy-name.proxy-xyz.us-east-1.rds.amazonaws.com", # Replace with write endpoint + "srw_read_endpoint": "rds-proxy-name-read-only.endpoint.proxy-xyz.us-east-1.rds.amazonaws.com", # Replace with read endpoint "srw_verify_new_connections": "True", # Enables role-verification for new endpoints "wrapper_dialect": "aurora-mysql", - "autocommit": True + "autocommit": True, } """ Setup step: open connection and create tables """ with AwsWrapperConnection.connect(mysql.connector.Connect, **params) as conn: configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)") + conn, + "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)", + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200) + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200) + ) """ Example step: open connection and perform transaction """ try: @@ -89,14 +93,22 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", ("Jane Doe",)) + conn, + "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", + ("Jane Doe",), + ) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", ("John Smith",)) + conn, + "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", + ("John Smith",), + ) # Internally switch to a reader connection conn.read_only = True for i in range(2): - cursor = execute_queries_with_failover_handling(conn, "SELECT * FROM bank_test WHERE id = %s", (i,)) + cursor = execute_queries_with_failover_handling( + conn, "SELECT * FROM bank_test WHERE id = %s", (i,) + ) for record in cursor: print(record) diff --git a/docs/examples/PGReadWriteSplitting.py b/docs/examples/PGReadWriteSplitting.py index 6e0a44c6..ee844b67 100644 --- a/docs/examples/PGReadWriteSplitting.py +++ b/docs/examples/PGReadWriteSplitting.py @@ -58,7 +58,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverSuccessError: # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoversuccesserror # The old cursor is no longer reusable and the application needs to reconfigure sessions states. configure_initial_session_states(conn) @@ -69,12 +69,12 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverFailedError as e: # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoverfailederror raise e except TransactionResolutionUnknownError as e: # User application should check the status of the failed transaction and restart it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#transactionresolutionunknownerror raise e @@ -86,7 +86,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O "password": "pwd", "plugins": "read_write_splitting,failover,host_monitoring", "wrapper_dialect": "aurora-pg", - "autocommit": True + "autocommit": True, } """ @@ -101,11 +101,15 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O with AwsWrapperConnection.connect(psycopg.Connection.connect, **params) as conn: configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)") + conn, + "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)", + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200) + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200) + ) """ Example step: open connection and perform transaction """ try: @@ -113,14 +117,22 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", ("Jane Doe",)) + conn, + "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", + ("Jane Doe",), + ) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", ("John Smith",)) + conn, + "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", + ("John Smith",), + ) # Internally switch to a reader connection conn.read_only = True for i in range(2): - cursor = execute_queries_with_failover_handling(conn, "SELECT * FROM bank_test WHERE id = %s", (i,)) + cursor = execute_queries_with_failover_handling( + conn, "SELECT * FROM bank_test WHERE id = %s", (i,) + ) results = cursor.fetchall() for record in results: print(record) diff --git a/docs/examples/PGSimpleReadWriteSplitting.py b/docs/examples/PGSimpleReadWriteSplitting.py index 216f69a0..82e0fca2 100644 --- a/docs/examples/PGSimpleReadWriteSplitting.py +++ b/docs/examples/PGSimpleReadWriteSplitting.py @@ -40,7 +40,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverSuccessError: # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoversuccesserror # The old cursor is no longer reusable and the application needs to reconfigure sessions states. configure_initial_session_states(conn) @@ -51,38 +51,42 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverFailedError as e: # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoverfailederror raise e except TransactionResolutionUnknownError as e: # User application should check the status of the failed transaction and restart it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#transactionresolutionunknownerror raise e if __name__ == "__main__": params = { - "host": "database.cluster-xyz.us-east-1.rds.amazonaws.com", + "host": "rds-proxy-name.proxy-xyz.us-east-1.rds.amazonaws.com", "dbname": "postgres", "user": "john", "password": "pwd", "plugins": "srw,failover", - "srw_write_endpoint": "database.cluster-xyz.us-east-1.rds.amazonaws.com", # Replace with write endpoint - "srw_read_endpoint": "database.cluster-ro-xyz.us-east-1.rds.amazonaws.com", # Replace with read endpoint + "srw_write_endpoint": "rds-proxy-name.proxy-xyz.us-east-1.rds.amazonaws.com", # Replace with write endpoint + "srw_read_endpoint": "rds-proxy-name-read-only.endpoint.proxy-xyz.us-east-1.rds.amazonaws.com", # Replace with read endpoint "srw_verify_new_connections": "True", # Enables role-verification for new endpoints "wrapper_dialect": "aurora-pg", - "autocommit": True + "autocommit": True, } """ Setup step: open connection and create tables """ with AwsWrapperConnection.connect(psycopg.Connection.connect, **params) as conn: configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)") + conn, + "CREATE TABLE IF NOT EXISTS bank_test (id int primary key, name varchar(40), account_balance int)", + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (0, "Jane Doe", 200) + ) execute_queries_with_failover_handling( - conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200)) + conn, "INSERT INTO bank_test VALUES (%s, %s, %s)", (1, "John Smith", 200) + ) """ Example step: open connection and perform transaction """ try: @@ -90,18 +94,26 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O configure_initial_session_states(conn) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", ("Jane Doe",)) + conn, + "UPDATE bank_test SET account_balance=account_balance - 100 WHERE name=%s", + ("Jane Doe",), + ) execute_queries_with_failover_handling( - conn, "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", ("John Smith",)) + conn, + "UPDATE bank_test SET account_balance=account_balance + 100 WHERE name=%s", + ("John Smith",), + ) # Internally switch to a reader connection conn.read_only = True for i in range(2): - cursor = execute_queries_with_failover_handling(conn, "SELECT * FROM bank_test WHERE id = %s", (i,)) + cursor = execute_queries_with_failover_handling( + conn, "SELECT * FROM bank_test WHERE id = %s", (i,) + ) results = cursor.fetchall() for record in results: print(record) - + finally: with AwsWrapperConnection.connect(psycopg.Connection.connect, **params) as conn: execute_queries_with_failover_handling(conn, "DROP TABLE bank_test") From 788f4e123a4f9abe55472a5d72eccf7da05090d4 Mon Sep 17 00:00:00 2001 From: Sophia Chu Date: Fri, 5 Dec 2025 16:08:04 -0800 Subject: [PATCH 3/3] fix: verification parameters --- docs/examples/PGFailover.py | 6 ++--- .../UsingTheSimpleReadWriteSplittingPlugin.md | 24 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/examples/PGFailover.py b/docs/examples/PGFailover.py index 2afb927f..e1ece5a5 100644 --- a/docs/examples/PGFailover.py +++ b/docs/examples/PGFailover.py @@ -43,7 +43,7 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverSuccessError: # Query execution failed and AWS Advanced Python Driver successfully failed over to an available instance. - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---successful-failover + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoversuccesserror # The old cursor is no longer reusable and the application needs to reconfigure sessions states. configure_initial_session_states(conn) @@ -54,12 +54,12 @@ def execute_queries_with_failover_handling(conn: Connection, sql: str, params: O except FailoverFailedError as e: # User application should open a new connection, check the results of the failed transaction and re-run it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#FailoverFailedError---unable-to-establish-sql-connection + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#failoverfailederror raise e except TransactionResolutionUnknownError as e: # User application should check the status of the failed transaction and restart it if needed. See: - # https://github.com/awslabs/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#TransactionResolutionUnknownError---transaction-resolution-unknown + # https://github.com/aws/aws-advanced-python-wrapper/blob/main/docs/using-the-python-driver/using-plugins/UsingTheFailoverPlugin.md#transactionresolutionunknownerror raise e diff --git a/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md b/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md index 76c7893b..45fbd73c 100644 --- a/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md +++ b/docs/using-the-python-driver/using-plugins/UsingTheSimpleReadWriteSplittingPlugin.md @@ -24,14 +24,22 @@ conn = AwsWrapperConnection.connect(psycopg.Connection.connect, **params) ``` ## Simple Read/Write Splitting Plugin Parameters -| Parameter | Value | Required | Description | Default Value | Example Values | -|--------------------------------------|:-------:|:--------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------------| -| `srw_write_endpoint` | String | Yes | The endpoint to connect to upon setting `read_only` to `False`. | `None` | `.cluster-..rds.amazonaws.com` | -| `srw_read_endpoint` | String | Yes | The endpoint to connect to upon setting `read_only` to `True`. | `None` | `.cluster-ro-..rds.amazonaws.com` | -| `srw_verify_new_connections` | Boolean | No | Enables writer/reader verification for new connections made by the Simple Read/Write Splitting Plugin. More details below. | `True` | `True`, `False` | -| `srw_verify_initial_connection_type` | String | No | If `srw_verify_new_connections` is set to `True`, this parameter will verify the initial opened connection to be either a writer or a reader. More details below. | `None` | `writer`, `reader` | -| `srw_connect_retry_timeout_ms` | Integer | No | If `srw_verify_new_connections` is set to `True`, this parameter sets the maximum allowed time in milliseconds for retrying connection attempts. More details below. | `6000` | `60000` | -| `srw_connect_retry_interval_ms` | Integer | No | If `srw_verify_new_connections` is set to `True`, this parameter sets the time delay in milliseconds between each retry of opening a connection. More details below. | `100` | `1000` | +| Parameter | Value | Required | Description | Default Value | Example Values | +|--------------------------------------|:-------:|:--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------------| +| `srw_write_endpoint` | String | Yes | The endpoint to connect to upon setting `read_only` to `False`. | `None` | `.cluster-..rds.amazonaws.com` | +| `srw_read_endpoint` | String | Yes | The endpoint to connect to upon setting `read_only` to `True`. | `None` | `.cluster-ro-..rds.amazonaws.com` | +| `srw_verify_new_connections` | Boolean | No | Enables writer/reader verification for new connections made by the Simple Read/Write Splitting Plugin. More details below. | `True` | `True`, `False` | +| `srw_verify_initial_connection_type` | String | No | If `srw_verify_new_connections` is set to `True` and this value is set, the wrapper will verify whether the initial connection matches the specified role. Valid values are 'reader' or 'writer'. More details below. | `None` | `writer`, `reader` | +| `srw_connect_retry_timeout_ms` | Integer | No | If `srw_verify_new_connections` is set to `True`, this parameter sets the maximum allowed time in milliseconds for retrying connection attempts. More details below. | `6000` | `60000` | +| `srw_connect_retry_interval_ms` | Integer | No | If `srw_verify_new_connections` is set to `True`, this parameter sets the time delay in milliseconds between each retry of opening a connection. More details below. | `100` | `1000` | + +> [!NOTE]\ +> The following values are only used when verifying new connections with the Simple Read/Write Splitting Plugin: +> - `srw_verify_initial_connection_type` +> - `srw_connect_retry_timeout_ms` +> - `srw_connect_retry_interval_ms` +> +> When `srw_verify_new_connections` is set to `False`, these values will have no impact. ## How the Simple Read/Write Splitting Plugin Verifies Connections