Narration transcript

AI-narrated listening adaptation. Complete code and sources remain in the written article.

Read the original article

A Highly Available Three-Tier Application on AWS: Multi-Region Design with Aurora DSQL

A Region failure is not just a database problem. The complete user journey still has to work: serve the page, authenticate the user, accept a request, and find the committed result afterward.

This article describes a proposed order-management application. Its front end is a static single-page application. Its application programming interface runs in containers. Its data tier needs transactions and consistent reads across Regions. Payments and large uploads have separate recovery contracts.

The reference design uses Ireland and London as application Regions, with Paris as the Aurora DSQL witness. London is in the United Kingdom. This placement is not an EU-only data-residency design. Check the jurisdiction requirements for application data, identity data, logs, and witness transaction logs before selecting Regions.

Start with the presentation tier.

Route 53 names point the application hostname to CloudFront. Two private S3 buckets contain the same static release. Use S3 REST origins and Origin Access Control. Keep public access blocked and restrict bucket policies to the intended distribution.

CloudFront origin failover is useful for eligible reads, including GET, HEAD, and OPTIONS. It does not provide general failover for POST or PUT. The application interface therefore uses a separate routing path.

Publish the content-hashed assets to both Regions before changing the entry HTML. A release should not depend on asynchronous bucket replication catching up. Verify the manifest, plan a rollback, and handle single-page application routes deliberately.

The application tier has two independent regional deployments.

The interface hostname points to a standard Global Accelerator. Regional endpoint groups contain internal application load balancers. Global Accelerator chooses endpoints using health, location, and routing controls. Traffic dials control participation; they do not guarantee an exact global split.

New connections can move after failure detection. Interrupted requests and existing connections are not replayed for you. Clients need bounded retries, and the application needs a stable business operation identifier.

Each load balancer spans three Availability Zones. Fargate tasks run privately, with ingress permitted from the load balancer security group. Configure regional certificates, target health, graceful shutdown, connection draining, and regional firewall rules.

Fargate fits an ordinary container workload without requiring an additional Kubernetes control plane. Kubernetes can be justified by your platform requirements, but it is not required merely because the design is highly available.

Stateless tasks must not own durable sessions or order state. Removing a task should not erase an accepted operation.

Capacity matters before the outage.

Deploying to two Regions does not mean the surviving Region can absorb the full workload. Reserve capacity for regional loss and for losing one Availability Zone within the surviving deployment. Validate database contention, connection creation, downstream limits, and tail latency too.

The article's task-capacity numbers are illustrative sizing inputs. They are not an AWS benchmark. Autoscaling cannot create instantaneous capacity during an evacuation.

Now consider the data tier.

Aurora DSQL provides regional read and write endpoints for strongly consistent logical data. The witness contributes coordination and durability. It does not serve application traffic and is not a third full serving database.

Active-active data access avoids making this application's recovery depend on promoting a new database writer. Cross-Region coordination still costs latency. A nearby endpoint does not mean every commit is local.

Database availability is also not the availability of the whole application. Authentication, routing, releases, capacity, clients, and external services remain part of the user journey.

Compatibility is a design gate.

DSQL exposes PostgreSQL-compatible interfaces, but that does not make every PostgreSQL application compatible. Review supported SQL, transaction behavior, migration constraints, and quotas against the workload before choosing it.

Use the client's business operation identifier as the identity of an order request. If that identifier appears again, read the committed record, verify ownership and the payload, and return the original result. Reject reuse with changed intent.

A timeout does not prove that a transaction failed.

Private connectivity and authentication require separate decisions.

Use the documented DSQL connection endpoint and private hostname. A management interface endpoint alone does not provide SQL connectivity. Place endpoint network interfaces across the intended Availability Zones and restrict database access to the task security group.

Authenticate new database connections with the application task role and the corresponding database-role privileges. Reserve administrative access for provisioning. Sign and connect using the documented hostname, validate the server certificate, and keep the trusted certificate bundle available in the image.

Connection pools belong to each application process. Generate credentials when opening a connection, rather than for every request. Recycle connections with jitter and design against the service's current connection and transaction limits.

Concurrency recovery must preserve the business operation.

When an optimistic transaction conflict requires a retry, retry the whole transaction with a bounded budget and jitter. Keep the operation identifier unchanged. Strongly consistent reads do not remove every business-invariant problem.

A retry block should contain database operations. Do not charge a payment card inside a transaction that may be retried. For uncertain commit outcomes, reconcile the original operation before repeating its effect.

For asynchronous effects, an outbox can preserve dispatch intent alongside the order transaction. Workers still need deduplication. A regional queue does not automatically become a globally replicated queue or guarantee exactly-once external effects.

Identity is another regional dependency.

The article describes Cognito primary and standby replication. Replication does not make every account operation active-active. Supported authentication methods, issuer configuration, signing-key access, and endpoint selection all matter during failover.

The application's Global Accelerator does not fail over Cognito on its own. Regional authentication clients need an explicit recovery path.

Plan a degraded identity experience for unsupported operations. Existing valid tokens can support authorized requests subject to their validity and policy, but that is different from promising every sign-in, password reset, registration, or profile change during an outage.

Release and recovery controls complete the design.

Check that each Region has the approved immutable image digest before release. Regional artifact replication is useful, but it is not proof of release readiness.

Private image pulls, logs, secrets, and external integrations need their own endpoint or egress design. Confirm coverage in the Availability Zones that must survive. Treat network expansion, quotas, and costs as operating constraints.

Backups solve a different problem from live replication. Replication can reproduce deletion or corruption. A restore creates a separate recovery workflow with endpoint changes, permissions, connectivity checks, and application validation.

Deploy one application Region at a time. Use backward-compatible schema changes because both Regions share database state. A regional code rollback cannot undo a destructive shared-data migration.

Finally, test user outcomes rather than process health alone.

Exercise regional and zonal loss, interrupted commits, repeated operation identifiers, identity recovery, and backup restoration. Probe each Region directly as well as the public hostname; global routing can hide a broken regional deployment.

Health checks are not a security boundary. Preserve authorization even when routing fails open or dependencies are unhealthy.

The practical outcome is an architecture with explicit recovery boundaries. It separates static delivery, regional application execution, consistent data access, identity recovery, and restoration after corruption.

The written article contains the full diagram, configuration fragments, code, sizing assumptions, and AWS source links. This listening version explains the proposed design. It does not claim that the architecture was deployed or that its recovery objectives were measured.

Audio library