Routing DataSource Three Root Causes
Symptom
Backend startup fails soon after a read/write datasource routing change, often with a misleading downstream message such as jpaSharedEM_entityManagerFactory unresolved or another entity-manager wiring error.
This failure class is documented directly in the current backend source:
lumie-backend/app/src/main/java/com/lumie/app/config/RoutingDataSourceConfig.javalumie-backend/app/src/main/resources/application.yamllumie-backend/app/src/test/java/com/lumie/app/config/RoutingDataSourceIntegrationTest.java
If startup hangs on Flyway CREATE INDEX CONCURRENTLY instead, use Flyway Monolith Migration Traps.
Real Root Causes
The 2026-05-20 incident note narrowed three upstream causes that can all masquerade as the same JPA bean failure:
- Hibernate dialect autodetect runs against
LazyConnectionDataSourceProxyand cannot read real JDBC metadata during entity-manager boot. AbstractRoutingDataSourceis constructed internally but never receivesafterPropertiesSet(), so the first JDBC use throwsDataSource router not initialized.- Flyway tries to derive its datasource from the lazy routing proxy instead of a raw primary datasource or dedicated migrator datasource.
Diagnosis
Check the settled contracts first.
application.yamlmust still pinspring.jpa.database-platform: org.hibernate.dialect.PostgreSQLDialect.RoutingDataSourceConfigmust still callrouting.afterPropertiesSet()before returningnew LazyConnectionDataSourceProxy(routing).RoutingDataSourceConfigmust still expose a dedicated@FlywayDataSourcebuilt from the primary URL plusspring.flyway.user/password.
The current config comments explicitly record why those lines exist, which makes them the best first-stop source of truth when this failure reappears.
Fix
- Restore the explicit PostgreSQL dialect if Hibernate is trying to infer it from the lazy proxy.
- Restore the
afterPropertiesSet()call if the router was inlined or refactored. - Keep Flyway on a raw migrator datasource rather than the routing proxy.
The present regression guard for this area is lumie-backend/app/src/test/java/com/lumie/app/config/RoutingDataSourceIntegrationTest.java, which exercises the bean graph and verifies primary, readonly, and non-transactional routing.
Prevention
- Treat downstream entity-manager wiring errors as symptoms. Walk up to the datasource and Hibernate boot path before changing bean names.
- Pin the dialect explicitly whenever the primary datasource is lazy.
- Keep one focused integration test for datasource routing and one separate guide for Flyway-specific startup failures.
Source Incident Detail
The May 20, 2026 source incident identified three real root causes behind what looked like one routing datasource startup failure. The point of the writeup is to avoid fixing the last visible Spring exception while leaving the upstream boot path broken.
| Root cause | Failure shape |
|---|---|
| missing explicit Hibernate dialect | Hibernate tried to infer the database through a lazy routing proxy |
missing afterPropertiesSet() | AbstractRoutingDataSource was returned before target datasource resolution was initialized |
| Flyway through the routing proxy | migration startup used the wrong datasource abstraction instead of a raw migrator datasource |
The diagnosis order matters:
- Start at the first datasource/Hibernate boot error, not the final repository or entity-manager symptom.
- Confirm
spring.jpa.database-platformis explicit. - Confirm the routing datasource is initialized before wrapping it in
LazyConnectionDataSourceProxy. - Confirm Flyway has a dedicated
@FlywayDataSourcebuilt from primary connection properties and migrator credentials.
The integration test named in this page exists because each root cause can otherwise reappear as a different downstream bean failure.