Skip to content

Commit 03a5353

Browse files
committed
Migrate Python Connector Examples
Adding the examples using the Aurora DSQL Python Connector (with the smoke tests), since using the connector is the preferred method and offers a more seamless user experience. This is the promoted default path on the documentation, and the samples should be consistent with this. Moved the boto3 examples to an `alternatives`, for use as needed. This also offers agentic benefits, as most agents are now able to leverage a single search space and cross-example contex can stay intact. It creates stronger retrieval since all the variants sit in the same space, and agents will quickly be able to locate the best example since by default, the documentation normally points to the samples repository.
1 parent 9b85195 commit 03a5353

34 files changed

Lines changed: 1415 additions & 45 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ The subdirectories contain code examples for connecting and using Aurora DSQL in
2222
| JavaScript | [node-postgres (standalone)](javascript/node-postgres/) |
2323
| JavaScript | [Postgres.js](javascript/postgres-js/) |
2424
| Python | [Jupyter](python/jupyter) |
25+
| Python | [asyncpg](python/asyncpg/) |
2526
| Python | [psycopg](python/psycopg/) |
2627
| Python | [psycopg2](python/psycopg2/) |
2728
| Python | [SQLAlchemy](python/sqlalchemy) |

python/asyncpg/README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Aurora DSQL with asyncpg
2+
3+
This example demonstrates how to use the Aurora DSQL Python Connector with asyncpg to connect to Amazon Aurora DSQL clusters and perform basic database operations.
4+
5+
Aurora DSQL is a distributed SQL database service that provides high availability and scalability for
6+
your PostgreSQL-compatible applications.
7+
Asyncpg is a popular PostgreSQL database library for Python that allows
8+
you to interact with PostgreSQL databases using Python code.
9+
10+
## About the code example
11+
12+
The example demonstrates a flexible connection approach that works for both admin and non-admin users:
13+
14+
* When connecting as an **admin user**, the example uses the `public` schema and generates an admin authentication
15+
token.
16+
* When connecting as a **non-admin user**, the example uses a custom `myschema` schema and generates a standard
17+
authentication token.
18+
19+
The code automatically detects the user type and adjusts its behavior accordingly.
20+
21+
## ⚠️ Important
22+
23+
* Running this code might result in charges to your AWS account.
24+
* We recommend that you grant your code least privilege. At most, grant only the
25+
minimum permissions required to perform the task. For more information, see
26+
[Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
27+
* This code is not tested in every AWS Region. For more information, see
28+
[AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
29+
30+
31+
## TLS connection configuration
32+
33+
This example uses direct TLS connections where supported, and verifies the server certificate is trusted. Verified SSL
34+
connections should be used where possible to ensure data security during transmission.
35+
36+
* Driver versions following the release of PostgreSQL 17 support direct TLS connections, bypassing the traditional
37+
PostgreSQL connection preamble
38+
* Direct TLS connections provide improved connection performance and enhanced security
39+
* Not all PostgreSQL drivers support direct TLS connections yet, or only in recent versions following PostgreSQL 17
40+
* Ensure your installed driver version supports direct TLS negotiation, or use a version that is at least as recent as
41+
the one used in this sample
42+
* If your driver doesn't support direct TLS connections, you may need to use the traditional preamble connection instead
43+
44+
45+
### Prerequisites
46+
47+
* You must have an AWS account, and have your default credentials and AWS Region
48+
configured as described in the
49+
[Globally configuring AWS SDKs and tools](https://docs.aws.amazon.com/credref/latest/refdocs/creds-config-files.html)
50+
guide.
51+
* [Python 3.10.0](https://www.python.org/) or later.
52+
* You must have an Aurora DSQL cluster. For information about creating an Aurora DSQL cluster, see the
53+
[Getting started with Aurora DSQL](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/getting-started.html)
54+
guide.
55+
* If connecting as a non-admin user, ensure the user is linked to an IAM role and is granted access to the `myschema`
56+
schema. See the
57+
[Using database roles with IAM roles](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/using-database-and-iam-roles.html)
58+
guide.
59+
60+
### Download the Amazon root certificate from the official trust store
61+
62+
Download the Amazon root certificate from the official trust store:
63+
64+
```
65+
wget https://www.amazontrust.com/repository/AmazonRootCA1.pem -O root.pem
66+
```
67+
68+
### Set up environment for examples
69+
70+
1. Create and activate a Python virtual environment:
71+
72+
```bash
73+
python3 -m venv .venv
74+
source .venv/bin/activate # Linux, macOS
75+
# or
76+
.venv\Scripts\activate # Windows
77+
```
78+
79+
2. Install the required packages for running the examples:
80+
81+
```bash
82+
pip install -r requirements.txt
83+
```
84+
85+
### Run the code
86+
87+
#### What the Examples Do
88+
89+
The example demonstrates the following operations:
90+
91+
- Opening a connection to an Aurora DSQL cluster
92+
- Creating a table
93+
- Inserting and querying data
94+
95+
The example is designed to work with both admin and non-admin users:
96+
97+
- When run as an admin user, it uses the `public` schema
98+
- When run as a non-admin user, it uses the `myschema` schema
99+
100+
**Note:** running the example will use actual resources in your AWS account and may incur charges.
101+
102+
The connection pool examples demonstrate:
103+
- Creating a connection pool for Aurora DSQL
104+
- Using async context managers for connection management
105+
- Performing database operations through the pool
106+
- Running multiple concurrent database operations
107+
- Using asyncio.gather() for parallel execution
108+
- Proper resource management with connection pools
109+
110+
#### Environment Cluster Details
111+
112+
Set environment variables for your cluster details:
113+
114+
```bash
115+
# e.g. "admin"
116+
export CLUSTER_USER="<your user>"
117+
118+
# e.g. "foo0bar1baz2quux3quuux4.dsql.us-east-1.on.aws"
119+
export CLUSTER_ENDPOINT="<your endpoint>"
120+
121+
# e.g. "us-east-1"
122+
export REGION="<your region>"
123+
```
124+
125+
#### Run the example:
126+
127+
```bash
128+
# Run example directly
129+
python src/example.py
130+
131+
# Run example using pytest
132+
pytest ./test/test_example.py
133+
134+
# Run all using pytest
135+
pytest ./test
136+
```
137+
138+
The examples contain comments explaining the code and the operations being performed.
139+
140+
## Additional resources
141+
142+
* [Amazon Aurora DSQL Documentation](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/what-is-aurora-dsql.html)
143+
* [Amazon Aurora DSQL Python Connector Documentation](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/SECTION_program-with-dsql-connector-for-python.html)
144+
* [Asyncpg Documentation](https://magicstack.github.io/asyncpg/current/)
145+
* [AWS SDK for Python (Boto3) Documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)
146+
147+
---
148+
149+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
150+
151+
SPDX-License-Identifier: MIT-0

python/asyncpg/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[tool.pytest.ini_options]
2+
pythonpath = [
3+
"src"
4+
]

python/asyncpg/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
aurora-dsql-python-connector>=0.2.0
2+
boto3>=1.35.74
3+
asyncpg>=0.30.0
4+
pytest>=8.0
5+
pytest-asyncio>=1.2.0

python/asyncpg/src/example.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
import asyncio
7+
import os
8+
9+
import aurora_dsql_asyncpg as dsql
10+
11+
12+
async def create_connection(cluster_user, cluster_endpoint, region):
13+
ssl_cert_path = "./root.pem"
14+
if not os.path.isfile(ssl_cert_path):
15+
raise FileNotFoundError(f"SSL certificate file not found: {ssl_cert_path}")
16+
17+
conn_params = {
18+
"database": "postgres",
19+
"user": cluster_user,
20+
"host": cluster_endpoint,
21+
"port": 5432,
22+
"region": region,
23+
"ssl": "verify-full",
24+
"sslrootcert": ssl_cert_path,
25+
}
26+
27+
# Make a connection to the cluster
28+
conn = await dsql.connect(**conn_params)
29+
30+
if cluster_user == "admin":
31+
schema = "public"
32+
else:
33+
schema = "myschema"
34+
35+
try:
36+
await conn.execute(f"SET search_path = {schema};")
37+
except Exception as e:
38+
await conn.close()
39+
raise e
40+
41+
return conn
42+
43+
44+
async def exercise_connection(conn):
45+
await conn.execute(
46+
"""
47+
CREATE TABLE IF NOT EXISTS owner(
48+
id uuid NOT NULL DEFAULT gen_random_uuid(),
49+
name varchar(30) NOT NULL,
50+
city varchar(80) NOT NULL,
51+
telephone varchar(20) DEFAULT NULL,
52+
PRIMARY KEY (id))
53+
"""
54+
)
55+
56+
# Insert some rows
57+
await conn.execute(
58+
"INSERT INTO owner(name, city, telephone) VALUES($1, $2, $3)",
59+
"John Doe",
60+
"Anytown",
61+
"555-555-1999",
62+
)
63+
64+
row = await conn.fetchrow("SELECT * FROM owner WHERE name=$1", "John Doe")
65+
66+
# Verify the result we got is what we inserted before
67+
assert row[0] is not None
68+
assert row[1] == "John Doe"
69+
assert row[2] == "Anytown"
70+
assert row[3] == "555-555-1999"
71+
72+
# Clean up the table after the example. If we run the example again
73+
# we do not have to worry about data inserted by previous runs
74+
await conn.execute("DELETE FROM owner WHERE name = $1", "John Doe")
75+
76+
77+
async def main():
78+
conn = None
79+
try:
80+
cluster_user = os.environ.get("CLUSTER_USER", None)
81+
assert cluster_user is not None, "CLUSTER_USER environment variable is not set"
82+
83+
cluster_endpoint = os.environ.get("CLUSTER_ENDPOINT", None)
84+
assert (
85+
cluster_endpoint is not None
86+
), "CLUSTER_ENDPOINT environment variable is not set"
87+
88+
region = os.environ.get("REGION", None)
89+
assert region is not None, "REGION environment variable is not set"
90+
91+
conn = await create_connection(cluster_user, cluster_endpoint, region)
92+
await exercise_connection(conn)
93+
finally:
94+
if conn is not None:
95+
await conn.close()
96+
97+
print("Connection exercised successfully")
98+
99+
100+
if __name__ == "__main__":
101+
asyncio.run(main())
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
import asyncio
7+
import os
8+
9+
import aurora_dsql_asyncpg as dsql
10+
11+
12+
async def connect_with_pool(cluster_user, cluster_endpoint, region):
13+
ssl_cert_path = "./root.pem"
14+
if not os.path.isfile(ssl_cert_path):
15+
raise FileNotFoundError(f"SSL certificate file not found: {ssl_cert_path}")
16+
17+
pool_params = {
18+
"database": "postgres",
19+
"user": cluster_user,
20+
"host": cluster_endpoint,
21+
"port": 5432,
22+
"region": region,
23+
"ssl": "verify-full",
24+
"sslrootcert": ssl_cert_path,
25+
"min_size": 2,
26+
"max_size": 5,
27+
}
28+
29+
pool = await dsql.create_pool(**pool_params)
30+
try:
31+
async with pool.acquire() as conn:
32+
result = await conn.fetchval("SELECT 1")
33+
assert result == 1
34+
finally:
35+
await pool.close()
36+
37+
38+
async def main():
39+
try:
40+
cluster_user = os.environ.get("CLUSTER_USER", None)
41+
assert cluster_user is not None, "CLUSTER_USER environment variable is not set"
42+
43+
cluster_endpoint = os.environ.get("CLUSTER_ENDPOINT", None)
44+
assert (
45+
cluster_endpoint is not None
46+
), "CLUSTER_ENDPOINT environment variable is not set"
47+
48+
region = os.environ.get("REGION", None)
49+
assert region is not None, "REGION environment variable is not set"
50+
51+
ssl_cert_path = "./root.pem"
52+
if not os.path.isfile(ssl_cert_path):
53+
raise FileNotFoundError(f"SSL certificate file not found: {ssl_cert_path}")
54+
55+
await connect_with_pool(cluster_user, cluster_endpoint, region)
56+
57+
finally:
58+
pass
59+
60+
print("Pool exercised successfully")
61+
62+
63+
if __name__ == "__main__":
64+
asyncio.run(main())

0 commit comments

Comments
 (0)