Commit 0033eda ("Apply transactionIsolationLock in EclipseLinkConnectionHandle as well", fixing #36165) changed
EclipseLinkConnectionHandle from a private static to a private inner class, and wrapped entityManager.unwrap(Connection.class) in
getConnection() with the EclipseLinkJpaDialect's singleton-scoped transactionIsolationLock.
Since EclipseLinkJpaDialect is registered as a Spring singleton bean, this lock is shared across all threads. Under load, this serializes all JDBC connection acquisitions. Only one thread at a time can call unwrap(Connection.class), regardless of how many connections are available in the pool.
The lock is unconditional, it serializes getConnection() across all threads, even when no thread is using a custom isolation level.
Impact:
Even for applications that never use custom transaction isolation levels, all threads serialize through a single lock point when acquiring JDBC connections. Even with a connection pool of N connections, only 1 thread can acquire a connection at a time, there by the remaining N-1 connections sit idle.
Under load this leads to severe throughput degradation, connection pool exhaustion (threads stall on the lock).
Steps to reproduce:
- Configure EclipseLinkJpaDialect with default settings (lazyDatabaseTransaction=false)
- Use a bounded connection pool (e.g., Tomcat JDBC, 8 connections)
- Use only ISOLATION_DEFAULT on all @transactional methods
- Run load test with concurrent requests
Expected: All pool connections used concurrently, throughput scales with pool size
Actual: Throughput equivalent to a pool of 1. All threads serialize through the lock.
Suggested fix: The lock in getConnection() should only be acquired when a concurrent custom isolation level transaction is actually in progress.
Related:
@jhoeller
Commit 0033eda ("Apply transactionIsolationLock in EclipseLinkConnectionHandle as well", fixing #36165) changed
EclipseLinkConnectionHandle from a private static to a private inner class, and wrapped entityManager.unwrap(Connection.class) in
getConnection() with the EclipseLinkJpaDialect's singleton-scoped transactionIsolationLock.
Since EclipseLinkJpaDialect is registered as a Spring singleton bean, this lock is shared across all threads. Under load, this serializes all JDBC connection acquisitions. Only one thread at a time can call unwrap(Connection.class), regardless of how many connections are available in the pool.
The lock is unconditional, it serializes getConnection() across all threads, even when no thread is using a custom isolation level.
Impact:
Even for applications that never use custom transaction isolation levels, all threads serialize through a single lock point when acquiring JDBC connections. Even with a connection pool of N connections, only 1 thread can acquire a connection at a time, there by the remaining N-1 connections sit idle.
Under load this leads to severe throughput degradation, connection pool exhaustion (threads stall on the lock).
Steps to reproduce:
Expected: All pool connections used concurrently, throughput scales with pool size
Actual: Throughput equivalent to a pool of 1. All threads serialize through the lock.
Suggested fix: The lock in getConnection() should only be acquired when a concurrent custom isolation level transaction is actually in progress.
Related:
EclipseLinkConnectionHandlecan fail against transaction isolation race condition #36165)@jhoeller