AWS Advanced JDBC Wrapper integration
common-module’s DataSource integrates the AWS Advanced JDBC Wrapper (4.x) so that consumers can opt into Aurora reader-instance routing for read-only transactions and topology-driven failover detection. Adoption is scheme-detected per consumer — flip a single configuration string to opt in.
Outcome for callers
Section titled “Outcome for callers”| Capability | Without wrapper (jdbc:postgresql:) | With wrapper (jdbc:aws-wrapper:postgresql:) |
|---|---|---|
| Read-only transactions | All hit the writer endpoint. | Auto-routed to Aurora reader instances via the readWriteSplitting plugin. |
| Failover detection latency | ~30 s, bounded by the JVM DNS cache TTL. | ~2–5 s, via Aurora’s topology API (failover2 plugin). |
| Failover exception surface | Generic PSQLException → HTTP 500. | Typed FailoverSuccess / TransactionStateUnknown / FailoverFailed exceptions → mapped to AppError.Transient → HTTP 503 + Retry-After: 2 (see API design — Transient failures and retry contract). Most failovers are absorbed by the in-process retry at the inTransaction boundary and the caller sees HTTP 200. |
Opting in
Section titled “Opting in”Set the consumer’s jdbcUrl to the wrapper scheme. Everything else is read by common-module from the URL prefix. No other configuration knob is required.
# application.conf — opted indataSource { db { url = "jdbc:aws-wrapper:postgresql://<cluster-endpoint>:5432/<db>" }}# application.conf — not opted in (zero behaviour change)dataSource { db { url = "jdbc:postgresql://<cluster-endpoint>:5432/<db>" }}The retry policy at the inTransaction boundary picks up dataSource.pool.maxAttempts (default 2) and dataSource.pool.backoffMs (default 300 ms) regardless of the URL scheme — but it only triggers when classification returns AppError.Transient, which can only happen when the wrapper is opted in.
How it works
Section titled “How it works”common-module’s DataSource detects the wrapper scheme and conditionally wires HikariCP through the wrapper:
The wrapper is a JDBC driver that sits between HikariCP and the PostgreSQL driver. It intercepts Connection.setReadOnly(boolean) (which Exposed already calls when application code passes readOnly = true to transaction(...)), and re-routes the physical connection accordingly. When the cluster topology changes, the wrapper raises typed exceptions that common-module classifies via Throwable.normalizeToAppError() into AppError.Transient subtypes.
The wrapper-specific HikariCP configuration (driver class, wrapperPlugins, exceptionOverrideClassName, Aurora tuning properties) is only set when isWrapperScheme = dbConfig.url.toString().startsWith("jdbc:aws-wrapper:") is true. Consumers on jdbc:postgresql: URLs run the same HikariCP setup as before PDEV-490.
Adopters
Section titled “Adopters”| Component | Opted in? | Notes |
|---|---|---|
operations | Yes (PDEV-490) | All module DBs (kanban, item, facility, station, business-affiliates, procurement-orders, batch) opt in via the Helm jdbcUrlFor template. |
accounts-component | No | Migration deferred until a similar performance / resilience driver appears. |
New consumers can opt in unilaterally — there is no coordination with common-module beyond depending on 8.4.0 or later.
Operational considerations
Section titled “Operational considerations”- First-request topology discovery cost. The wrapper’s topology cache is built lazily on first use. The first request after a pod cold start may pay a ~50–100 ms topology-discovery cost.
auroraInitialConnectionplugin in the pipeline keeps this bounded;failoverClusterTopologyRefreshRateMs = 2000keeps the cache fresh thereafter. - HikariCP cooperation.
exceptionOverrideClassName = software.amazon.jdbc.util.HikariCPSQLExceptionis set when opted in; this tells HikariCP to keep connections alive that the wrapper marks as recoverable (e.g. during a failover window). Without this, HikariCP would evict the pool on every transient SQL exception and pay full reconnect cost. - RDS Proxy is incompatible. The wrapper expects direct connections to Aurora cluster endpoints; routing through RDS Proxy bypasses the topology discovery and read-write splitting. PDEV-499 closed RDS Proxy adoption as won’t-do for this reason.
See also
Section titled “See also”- API design — Transient failures and retry contract — the HTTP 503 +
Retry-After: 2contract emitted when the wrapper raises a failover exception that exhausts the retry budget. - Bitemporal composite indexes — the index shape paired with the wrapper’s reader routing to keep p95 down on latest-version-per-eId reads; the optimisation that operations adopted under PDEV-490.
- Bitemporal persistence — the access pattern itself (valid + transaction time, latest-version-per-eId SQL); the conceptual model the wrapper’s read-routing applies to.
- Flyway is authoritative for database indexes —
common-module’sDbMigrationis configured withmixed=true+ non-transactional advisory lock so the operations-sideCREATE INDEX CONCURRENTLYmigrations introduced under PDEV-490 coexist with the existing transactional migration tree. - External: AWS Advanced JDBC Wrapper documentation.
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved