AWS Database Blog

Building async Python applications with Tortoise ORM and Amazon Aurora DSQL

High concurrency async Python applications like rideshare platforms handle large volumes of simultaneous database operations. Tortoise ORM paired with Amazon Aurora DSQL gives you a strong foundation for building them. With Python’s asyncio framework, you can use cooperative multitasking: when one coroutine awaits a database response, others proceed without blocking.

Tortoise ORM is an async-native Python ORM inspired by Django’s model syntax. It’s built on asyncio from the ground up. Every database operation is a native coroutine with no bridging overhead. With Tortoise ORM’s asyncpg backend, you connect directly to the serverless architecture of Aurora DSQL, where connection efficiency and concurrency matter.

In this post, you build a working rideshare application that demonstrates reusable patterns for connecting Tortoise ORM to Aurora DSQL. You will learn how to:

  • Define Tortoise ORM models using UUID primary keys compatible with the distributed architecture of Aurora DSQL.
  • Manage table relationships at the application level using UUID columns to minimize optimistic concurrency control (OCC) conflicts.
  • Connect through AWS Identity and Access Management (IAM) authentication using Boto3’s DSQL client with asyncpg’s connection pool patched for compatibility.
  • Handle optimistic concurrency control (OCC) in Aurora DSQL with async retry logic and exponential backoff with jitter.
  • Run async CRUD operations for a rideshare domain: riders, drivers, rides, and payments.

Why Tortoise ORM with Aurora DSQL?

Tortoise ORM is a purpose-built async-native Python ORM, and pairing it with the serverless architecture of Aurora DSQL creates a stack optimized for high-concurrency Python applications. Here’s why this combination works:

Django-like model syntax with minimal adaptation: If your team knows Django, they already know Tortoise ORM. Model definitions use familiar field types (CharField, UUIDField, DecimalField), and the query API mirrors Django’s (filter(), get(), create()). The main adaptation for Aurora DSQL is using UUID primary keys for distributed-friendly key generation, with relationships modeled as UUID columns in this sample.

asyncpg driver for PostgreSQL: Tortoise ORM’s asyncpg backend connects directly to Aurora DSQL over PostgreSQL’s wire protocol. The asyncpg library is an async PostgreSQL driver that uses PostgreSQL’s binary protocol for efficient data transfer.

Clean OCC retry integration: Python’s async/await control flow makes retry logic straightforward. You wrap operations in an async retry function with exponential backoff, with no framework-specific annotations or decorators required.

Zero database credential management: IAM tokens generated through Boto3’s DSQL client replace stored passwords entirely. Combined with the serverless scaling of Aurora DSQL, you remove both credential rotation and capacity planning.

Solution overview

You will create an async Python application that models a rideshare platform with the following entities:

  • Riders: users who request rides.
  • Drivers: users who fulfill ride requests.
  • Rides: trip records linking riders to drivers with status tracking.
  • Payments: payment records associated with completed rides.

The following diagram shows the application architecture. An async Python application built with Tortoise ORM connects through the asyncpg driver to an Amazon Aurora DSQL cluster over a TLS-encrypted connection. The application authenticates with short-lived IAM tokens generated by the Boto3 DSQL client instead of a static password. The Tortoise ORM models (Riders, Drivers, Rides, and Payments) map to tables in Aurora DSQL. An OCC retry layer wraps write operations to handle concurrency conflicts.

Async Python application using Tortoise ORM connecting through asyncpg and IAM tokens to an Amazon Aurora DSQL cluster

Figure 1: Tortoise ORM application architecture with Amazon Aurora DSQL

When you run the application:

  1. The application generates an IAM authentication token using boto3’s DSQL client and patches asyncpg’s connection reset.
  2. Tortoise ORM initializes with the asyncpg backend using the IAM token as the password and TLS enabled.
  3. Schema creation executes each DDL statement individually through a raw asyncpg connection (Aurora DSQL doesn’t support multiple DDL in one transaction).
  4. The application runs async CRUD operations demonstrating the full rideshare workflow: creating riders and drivers, requesting rides, completing trips, and processing payments.
  5. OCC retry logic handles any concurrent write conflicts with exponential backoff and jitter.

Key considerations

When you use any ORM with the distributed architecture of Aurora DSQL, you need to make specific adaptations. The migration guide covers additional patterns.

  • UUID primary keys: Aurora DSQL recommends UUID V4 because they require no coordination across the distributed system. Tortoise ORM supports UUID fields natively with fields.UUIDField(primary_key=True, default=uuid.uuid4). Aurora DSQL also supports sequences and identity columns (with CACHE specified) when you need integer identifiers.
  • Choosing a relationship strategy: With the DSQL foreign key feature, you can enforce business logic as a constraint shared by a parent and child column. For example, you can prevent a product-id from being deleted while its related product-description lookup table still contains the same id. This sample instead uses UUIDField columns and manages relationships in application logic. Both approaches work well on Aurora DSQL: choose the one that fits your application’s needs.
  • Asyncpg connection pool reset: The asyncpg driver calls pg_advisory_unlock_all() when returning connections to the pool. Advisory locks aren’t available in Aurora DSQL. You must patch asyncpg’s Connection.reset() to a narrowed version that executes RESET ALL while skipping the unsupported advisory lock call.
  • Single DDL per transaction: Multiple DDL statements in a single transaction aren’t supported. Each CREATE TABLE must execute as its own statement using a raw connection.
  • No SAVEPOINT support: The SAVEPOINT command isn’t available in Aurora DSQL. Avoid nested transactions in your Tortoise ORM operations.
  • IAM authentication: Aurora DSQL uses time-limited IAM tokens instead of static passwords. The Boto3 DSQL client generates tokens through generate_db_connect_admin_auth_token() for admin users and generate_db_connect_auth_token() for non-admin application users. Generate a fresh token locally for each new connection request. Tokens are short-lived and disposable.
  • Optimistic concurrency control: When two transactions modify the same row concurrently, one receives a serialization error (SQLSTATE 40001, error code OC000 or OC001). Your application must implement retry logic. Both error codes are safe to retry.

Prerequisites

Before you begin, make sure that you have the following:

IAM permissions configuration

Use the following IAM policy. ClusterSetup covers the one-time cluster creation step. DatabaseAccess is what your application uses at runtime.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ClusterSetup",
      "Effect": "Allow",
      "Action": [
        "dsql:CreateCluster",
        "dsql:GetCluster",
        "dsql:TagResource"
      ],
      "Resource": "arn:aws:dsql:<region>:<account-id>:cluster/*"
    },
    {
      "Sid": "DatabaseAccess",
      "Effect": "Allow",
      "Action": [
        "dsql:DbConnectAdmin",
        "dsql:DbConnect"
      ],
      "Resource": "arn:aws:dsql:<region>:<account-id>:cluster/*"
    }
  ]
}

Replace region and account-id with your values. The dsql:DbConnectAdmin action is used when connecting as the admin user. The dsql:DbConnect action is used when connecting as a non-admin application user (Step 6).

Important: After creating your cluster, scope both statements to your specific cluster ARN (replace /* with /<cluster-id>) and remove dsql:CreateCluster. The wildcard character is used initially because the cluster ID isn’t known before creation.

Estimated time and costs

This tutorial takes 15–20 minutes. Running the sample generates minimal database activity. Aurora DSQL charges for Distributed Processing Units (DPUs) and storage (GiB-month). See Amazon Aurora DSQL Pricing. Delete your cluster after testing.

Solution walkthrough

In this walkthrough, you create an Aurora DSQL cluster, clone the sample application, and configure Tortoise ORM to work with Aurora DSQL. You then define the data models, set up the connection, add optimistic concurrency control (OCC) retry logic, and run the rideshare application. The following steps walk through each part in order.

Step 1: Create an Aurora DSQL cluster

First, configure an AWS CLI named profile for the IAM user that will interact with Aurora DSQL. This keeps your DSQL credentials isolated from other AWS profiles:

aws configure --profile dsql-user

This command prompts for the AWS Access Key ID and AWS Secret Access Key you generated when you created the IAM user for this tutorial. It also prompts for the default AWS Region (for example, us-east-1) and output format (json). Verify the profile is configured correctly:

aws sts get-caller-identity --profile dsql-user

You should see your account ID, IAM user ARN, and user ID in the response.

Now create a single-Region Aurora DSQL cluster using the AWS CLI:

aws dsql create-cluster \
  --region <region> \
  --deletion-protection-enabled \
  --tags Key=Name,Value=tortoise-rideshare-demo

Note the cluster ID from the output. Retrieve the endpoint:

aws dsql get-cluster --identifier <cluster-id> --region <region>

Wait for the status field to show ACTIVE. The endpoint field is the host name your application connects to (for example, abc123def456.dsql.us-east-1.on.aws).

Step 2: Clone the sample and install dependencies

Clone the repository:

git clone https://github.com/aws-samples/aurora-dsql-samples.git
cd aurora-dsql-samples/python/tortoise-orm

Create a virtual environment and install dependencies:

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

These are the only dependencies needed. Tortoise ORM’s asyncpg backend connects directly to Aurora DSQL using IAM tokens generated by Boto3. No additional adapter package is required.

Note: If you want to run the sample immediately, skip ahead to “Step 7: Run the application”. The following sections walk through the key parts of the code (model definitions, connection setup, and retry logic) to explain the adaptations specific to Aurora DSQL in this sample.

Step 3: Define the Tortoise ORM models

In src/rider_models.py, you define four tables using Tortoise ORM’s Django-like model syntax. All use UUID primary keys.

from tortoise import fields, models
import uuid


class Rider(models.Model):
    """A rider who requests rides."""
    id = fields.UUIDField(primary_key=True, default=uuid.uuid4)
    name = fields.CharField(max_length=100)
    email = fields.CharField(max_length=150, unique=True)
    phone = fields.CharField(max_length=20)
    rating = fields.DecimalField(max_digits=3, decimal_places=2, default=5.00)
    created_at = fields.DatetimeField(auto_now_add=True)

    class Meta:
        table = "rider"


class Driver(models.Model):
    """A driver who fulfills ride requests."""
    id = fields.UUIDField(primary_key=True, default=uuid.uuid4)
    name = fields.CharField(max_length=100)
    email = fields.CharField(max_length=150, unique=True)
    phone = fields.CharField(max_length=20)
    license_plate = fields.CharField(max_length=20)
    vehicle_model = fields.CharField(max_length=50)
    rating = fields.DecimalField(max_digits=3, decimal_places=2, default=5.00)
    is_available = fields.BooleanField(default=True)
    created_at = fields.DatetimeField(auto_now_add=True)

    class Meta:
        table = "driver"


class Ride(models.Model):
    """A ride connecting a rider to a driver."""
    id = fields.UUIDField(primary_key=True, default=uuid.uuid4)
    rider_id = fields.UUIDField()
    driver_id = fields.UUIDField(null=True)
    pickup_location = fields.CharField(max_length=200)
    dropoff_location = fields.CharField(max_length=200)
    status = fields.CharField(max_length=20, default="requested")
    fare_amount = fields.DecimalField(max_digits=10, decimal_places=2, null=True)
    requested_at = fields.DatetimeField(auto_now_add=True)
    completed_at = fields.DatetimeField(null=True)

    class Meta:
        table = "ride"


class Payment(models.Model):
    """A payment for a completed ride."""
    id = fields.UUIDField(primary_key=True, default=uuid.uuid4)
    ride_id = fields.UUIDField()
    rider_id = fields.UUIDField()
    amount = fields.DecimalField(max_digits=10, decimal_places=2)
    payment_method = fields.CharField(max_length=30)
    status = fields.CharField(max_length=20, default="pending")
    processed_at = fields.DatetimeField(null=True)
    created_at = fields.DatetimeField(auto_now_add=True)

    class Meta:
        table = "payment"

Key design decisions:

  • Each model uses fields.UUIDField(primary_key=True, default=uuid.uuid4). Aurora DSQL recommends UUIDs because they require no coordination across the distributed system.
  • Relationships are represented as UUIDField columns (such as rider_id, driver_id) rather than ForeignKeyField, giving you explicit control over joins.
  • Status fields use CharField with application-level validation rather than database-level enums.

Step 4: Configure the Aurora DSQL connection

In src/rider_config.py, you configure IAM authentication, patch asyncpg, and set up schema creation. The code supports both admin and non-admin users:

import os
import re
import ssl as ssl_module

import asyncpg
import boto3
from tortoise import Tortoise

CLUSTER_ENDPOINT = os.environ["CLUSTER_ENDPOINT"]
CLUSTER_REGION = os.environ.get("CLUSTER_REGION", "us-east-1")
CLUSTER_USER = os.environ.get("CLUSTER_USER")
if not CLUSTER_USER:
    raise ValueError(
        "Missing required environment variable CLUSTER_USER. "
        "Set to 'admin' for admin access or your app role name (e.g., 'rideshare_app') for non-admin."
    )

# Admin uses "public" schema; non-admin users use a custom schema
ADMIN_USER = "admin"
SCHEMA = "public" if CLUSTER_USER == ADMIN_USER else os.environ.get("CLUSTER_SCHEMA", "rideshare")

# Validate schema name to prevent SQL injection via environment variable tampering
if not re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]*', SCHEMA):
    raise ValueError(f"Invalid schema name: '{SCHEMA}'. Must be alphanumeric/underscore only.")

def generate_auth_token() -> str:
    """Generate a short-lived IAM authentication token for Aurora DSQL."""
    client = boto3.client("dsql", region_name=CLUSTER_REGION)
    if CLUSTER_USER == ADMIN_USER:
        token = client.generate_db_connect_admin_auth_token(
            CLUSTER_ENDPOINT, CLUSTER_REGION
        )
    else:
        token = client.generate_db_connect_auth_token(
            CLUSTER_ENDPOINT, CLUSTER_REGION
        )
    return token

async def _dsql_safe_reset(self, *, timeout=None):
    """Narrowed reset for Aurora DSQL compatibility.

    asyncpg's default Connection.reset() calls pg_advisory_unlock_all(),
    which Aurora DSQL does not support. This replacement performs session
    cleanup (RESET ALL) while skipping the unsupported advisory lock call.
    """
    await self.execute("RESET ALL")

async def init_db():
    """Initialize Tortoise ORM with Aurora DSQL connection."""
    token = generate_auth_token()

    # Patch asyncpg Connection.reset to skip pg_advisory_unlock_all
    asyncpg.connection.Connection.reset = _dsql_safe_reset

    # Aurora DSQL requires TLS connections
    ssl_ctx = ssl_module.create_default_context()

    await Tortoise.init(
        config={
            "connections": {
                "default": {
                    "engine": "tortoise.backends.asyncpg",
                    "credentials": {
                        "host": CLUSTER_ENDPOINT,
                        "port": 5432,
                        "user": CLUSTER_USER,
                        "password": token,
                        "database": "postgres",
                        "ssl": ssl_ctx,
                        "schema": SCHEMA,
                    },
                }
            },
            "apps": {
                "rideshare": {
                    "models": ["rider_models"],
                    "default_connection": "default",
                }
            },
        }
    )

Key points:

  • generate_auth_token() auto-detects admin and non-admin users: admin uses generate_db_connect_admin_auth_token(), non-admin uses generate_db_connect_auth_token().
  • _dsql_safe_reset() patches asyncpg’s Connection.reset() method. Without this patch, asyncpg calls pg_advisory_unlock_all() when returning connections to the pool, which Aurora DSQL doesn’t support. The replacement executes RESET ALL to clear session-level settings while skipping the unsupported advisory lock call.
  • The connection uses ssl_module.create_default_context() because Aurora DSQL requires TLS for all connections.
  • Schema creation runs each DDL statement individually with SET search_path TO {SCHEMA}, using a raw asyncpg.connect(). Aurora DSQL does not allow multiple DDL statements per transaction.

Step 5: Implement OCC retry logic

In src/retry.py, a reusable utility handles optimistic concurrency control in Aurora DSQL:

import asyncio
import random
from typing import TypeVar, Callable, Awaitable

from asyncpg import PostgresError

T = TypeVar("T")
BASE_DELAY = 0.05  # 50ms

# SQLSTATE code for serialization failure (OCC conflict)
OCC_SQLSTATE = "40001"

def _is_occ_error(e: Exception) -> bool:
    """Check if an exception is an OCC conflict using structured SQLSTATE inspection."""
    if isinstance(e, PostgresError):
        return e.sqlstate == OCC_SQLSTATE
    # Fallback for wrapped exceptions (e.g., Tortoise OperationalError).
    # Match DSQL-specific OCC error codes with word boundaries to avoid
    # false positives from unrelated messages containing these substrings.
    error_msg = str(e)
    return "SQLSTATE 40001" in error_msg or "OC000" in error_msg or "OC001" in error_msg

async def with_retry(
    fn: Callable[[], Awaitable[T]],
    max_retries: int = 3,
) -> T:
    """Retry an async operation on OCC conflict with exponential backoff and jitter."""
    for attempt in range(max_retries + 1):
        try:
            return await fn()
        except Exception as e:
            if _is_occ_error(e) and attempt < max_retries:
                backoff = BASE_DELAY * (2 ** attempt)
                jitter = random.uniform(0, backoff)
                await asyncio.sleep(backoff + jitter)
            else:
                raise

Usage in the application:

from retry import with_retry

async def _update_rating():
    """Re-read and update : the retry-safe pattern."""
    d = await Driver.get(id=driver_id)
    d.rating = Decimal("4.85")
    await d.save(update_fields=["rating"])
    return d

updated = await with_retry(_update_rating)

Design principles:

  • Re-read before retry: The function must fetch the latest state from the database on each invocation. Stale reads cause repeated conflicts.
  • Exponential backoff with jitter: Wait times increase exponentially (50 ms, then 100 ms, then 200 ms) with random jitter added. This prevents concurrent retries from colliding again (the thundering herd problem).
  • Scoped updates: Use Tortoise ORM’s update_fields parameter to write only the columns you changed, reducing the conflict surface.

Step 6: Set up a non-admin application user (optional)

For production workloads, use a least-privilege application user instead of admin. In Aurora DSQL, every non-admin database user maps 1:1 to an IAM identity through the AWS IAM GRANT command.

Note: This step requires an IAM user with permissions to create IAM policies and modify Aurora DSQL cluster access.

1. Verify your IAM user has dsql:DbConnect

Your IAM policy’s DatabaseAccess statement (from Prerequisites) should include both dsql:DbConnectAdmin and dsql:DbConnect.

2. Create the database role and schema (connect as admin)

Open src/setup_app_user.sql. Replace cluster-endpoint and region with your values. Then run:

TOKEN=$(aws dsql generate-db-connect-admin-auth-token \
  --hostname <cluster-endpoint> \
  --region <region> \
  --expires-in 3600)

psql "host=<cluster-endpoint> port=5432 dbname=postgres user=admin sslmode=require password=${TOKEN}" \
  -f src/setup_app_user.sql

This creates a rideshare_app database role, links it to your IAM identity with AWS IAM GRANT, and sets up a rideshare schema with CRUD permissions.

3. Connect as the non-admin user

Set the environment variables and run:

export CLUSTER_ENDPOINT="your-cluster.dsql.your-region.on.aws"
export CLUSTER_REGION="your-region"
export CLUSTER_USER="rideshare_app"
export CLUSTER_SCHEMA="rideshare"

cd src/

# Test connectivity with the app user
python test_connection.py

# Run the full demo
python riders_app.py

The code automatically selects the correct token method based on whether CLUSTER_USER is “admin” or any other value.

Step 7: Run the application (admin user)

Set environment variables and run:

export CLUSTER_ENDPOINT="your-cluster.dsql.your-region.on.aws"
export CLUSTER_REGION="your-region"
export CLUSTER_USER="admin"

cd src/

# Test connectivity
python test_connection.py

# Run the full demo
python riders_app.py

When you run riders_app.py, the application executes the complete rideshare workflow against your Aurora DSQL cluster and logs its progress to the console. You will see it initialize the connection and create the schema, then seed a set of riders and drivers. Next, it simulates several rides end to end: requesting each ride, matching it to a driver, completing the trip, and processing payment. It then queries the data back to display ride history per rider and completed-ride statistics per driver. It also demonstrates the OCC retry pattern by updating a driver’s rating through the retry-safe utility. Finally, it prints a summary of total rides, revenue, and payments, and confirms the demo completed successfully. After reviewing this output and the data it created, proceed to clean up the demo data.

Note: If you see “Missing required environment variable CLUSTER_ENDPOINT”, check that your environment variables are set in the current shell session.

Step 8: Clean up demo data

After reviewing the tables and data created by the demo, run the cleanup script:

cd src/
python cleanup.py

Security considerations

For production deployments, apply these security best practices:

  • Token-based authentication: Aurora DSQL replaces static database passwords with short-lived IAM tokens generated at runtime. In this sample, Boto3’s DSQL client produces tokens valid for 15 minutes. Scope the dsql:DbConnectAdmin action to your specific cluster ARN rather than using wildcards, and separate admin credentials (for schema migrations) from application credentials (for runtime operations).
  • TLS encryption in transit: Aurora DSQL requires TLS for all connections. The sample uses Python’s ssl.create_default_context() which validates server certificates against system CA roots. Never turn off certificate verification in production.
  • Network isolation: Deploy your application within an Amazon Virtual Private Cloud (Amazon VPC) and use VPC endpoints to reach Aurora DSQL without traversing the public internet.
  • Principle of least privilege: Grant only dsql:DbConnect to your application’s runtime IAM role. Reserve dsql:DbConnectAdmin exclusively for schema setup or migration roles. Avoid broad actions like dsql:* on your roles.
  • Credential hygiene: Use IAM roles (Amazon Elastic Compute Cloud (Amazon EC2) instance profiles, Amazon Elastic Container Service (Amazon ECS) task roles, or IAM Roles Anywhere) rather than long-lived access keys. The boto3 credential chain resolves credentials automatically without embedding secrets in your application.

Production considerations

  • Connection pool tuning: Tortoise ORM’s asyncpg backend uses a connection pool. Configure minsize and maxsize in the connection credentials based on your expected concurrency. Monitor pool utilization to right-size these values.
  • Token refresh for long-running applications: An IAM authentication token is used only to establish a connection. It’s passed as the password when the connection opens. After a connection is established, it remains valid even after the token expires, up to the maximum connection duration of Aurora DSQL (60 minutes) or until it’s closed. You only need a new token when opening a new connection. For a pooled application, generate a fresh token each time the pool creates or replaces a connection, rather than reusing one token for the pool’s lifetime. The token’s own lifetime is set with the expires-in option, which defaults to 15 minutes and can be extended up to 7 days.
  • Observability: Add structured logging with correlation IDs for request tracing. Monitor connection pool health, OCC retry counts, and token refresh success/failure rates with Amazon CloudWatch metrics.
  • Health checks: Implement a health check endpoint that verifies database connectivity with a lightweight SELECT 1 query, so your load balancer can detect unhealthy instances.

Aurora DSQL service limits: Design your application within the cluster quotas and database limits of Aurora DSQL. Each cluster supports up to 10,000 active connections at a rate of 100 new connections per second (burst capacity 1,000). A single connection stays open for a maximum of 60 minutes. Size your connection pool and plan connection refresh accordingly. For write operations, a single transaction can modify up to 3,000 rows and 10 MiB of data and can run for a maximum of 5 minutes. Design batch writes to stay within these bounds. For the complete list, see Cluster quotas and database limits in Amazon Aurora DSQL.

Clean up

To avoid ongoing charges for resources created in this walkthrough, remove them when you’re finished.

To delete the Aurora DSQL cluster

Using the AWS CLI:

# Remove deletion protection first
aws dsql update-cluster \
  --region <region> \
  --identifier <cluster-id> \
  --no-deletion-protection-enabled

# Delete the cluster
aws dsql delete-cluster \
  --region <region> \
  --identifier <cluster-id>

To remove database tables without deleting the cluster

DROP TABLE IF EXISTS payment;
DROP TABLE IF EXISTS ride;
DROP TABLE IF EXISTS driver;
DROP TABLE IF EXISTS rider;

Conclusion

In this post, you built an async rideshare application using Tortoise ORM and Amazon Aurora DSQL. The required adaptations are minimal: UUID primary keys, asyncpg pool patching, individual DDL execution, and OCC retry. These patterns apply broadly to any async Python application on Aurora DSQL. The combination of Tortoise ORM’s native coroutine-based queries and the serverless scalability of Aurora DSQL makes this stack a strong choice for high-concurrency workloads. Examples include rideshare, delivery tracking, real-time marketplaces, and IoT event processing.

To take this further, deploy the application across multiple Regions using multi-Region clusters in Aurora DSQL for active-active writes. You can also connect as a non-admin user with a custom schema to test multi-tenant patterns.

Get started by cloning the aurora-dsql-samples repository and deploying the sample against your own Aurora DSQL cluster. For more on Aurora DSQL, see the Aurora DSQL User Guide. For Tortoise ORM documentation, visit tortoise.github.io. To learn more, refer to Introducing Amazon Aurora DSQL and Amazon Aurora DSQL for global-scale financial transactions on the AWS Database Blog.


About the authors

Lasita Bhattacharya

Lasita Bhattacharya

Lasita is a Technical Account Manager at AWS, where she partners with enterprise customers to improve and modernize their cloud workloads. Lasita specializes in database and cloud operation, helping teams build resilient, scalable systems.

Sushant Deshmukh

Sushant Deshmukh

Sushant is a Senior Partner Solutions Architect at AWS. He is deeply engaged in the generative AI space — advising partners on building production-grade AI solutions with Amazon Bedrock, agentic AI patterns, and data foundations that power intelligent workloads. Outside of work, he enjoys traveling to explore new places and cuisines, playing volleyball, and spending quality time with family and friends.