Skip to main content

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.java
  • lumie-backend/app/src/main/resources/application.yaml
  • lumie-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:

  1. Hibernate dialect autodetect runs against LazyConnectionDataSourceProxy and cannot read real JDBC metadata during entity-manager boot.
  2. AbstractRoutingDataSource is constructed internally but never receives afterPropertiesSet(), so the first JDBC use throws DataSource router not initialized.
  3. 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.yaml must still pin spring.jpa.database-platform: org.hibernate.dialect.PostgreSQLDialect.
  • RoutingDataSourceConfig must still call routing.afterPropertiesSet() before returning new LazyConnectionDataSourceProxy(routing).
  • RoutingDataSourceConfig must still expose a dedicated @FlywayDataSource built from the primary URL plus spring.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 causeFailure shape
missing explicit Hibernate dialectHibernate 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 proxymigration startup used the wrong datasource abstraction instead of a raw migrator datasource

The diagnosis order matters:

  1. Start at the first datasource/Hibernate boot error, not the final repository or entity-manager symptom.
  2. Confirm spring.jpa.database-platform is explicit.
  3. Confirm the routing datasource is initialized before wrapping it in LazyConnectionDataSourceProxy.
  4. Confirm Flyway has a dedicated @FlywayDataSource built 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.