-
Bug
-
Resolution: Won't Do
-
Major
-
None
-
EAP_EWP 5.1.2
-
None
-
NEW
https://hibernate.onjira.com/browse/HHH-7910
If a transaction times out while fetching, the thread that is fetching will throw the following exception: org.hibernate.AssertionFailure: possible non-threadsafe access to the session.
It is indeed a non-threadsafe access to the session, but triggered indirectly by hibernate itself.
What happens is that when an entity manager gets created, it will register with the current jta transaction:
Thread [WorkerThread#0[10.23.132.245:61617]] (Suspended (entry into method joinTransaction in AbstractEntityManagerImpl)) EntityManagerImpl(AbstractEntityManagerImpl).joinTransaction(boolean) line: 436 EntityManagerImpl(AbstractEntityManagerImpl).postInit() line: 82 EntityManagerImpl.<init>(SessionFactory, PersistenceContextType, PersistenceUnitTransactionType, boolean, Class, Map) line: 62 EntityManagerFactoryImpl.createEntityManager(Map) line: 40 EntityManagerFactoryImpl.createEntityManager() line: 35
as a result, a jta synchronization gets registered with code like:
public void afterCompletion(int status) { try { if(STATUS_ROLLEDBACK == status && transactionType == PersistenceUnitTransactionType.JTA && session.isOpen()) session.clear();
When the transaction times out, the after completion will get called, which will clear the session, like in this stack (reaper thread):
Daemon Thread [Thread-47] (Suspended (entry into method clear in StatefulPersistenceContext)) StatefulPersistenceContext.clear() line: 202 SessionImpl.clear() line: 284 AbstractEntityManagerImpl$1.afterCompletion(int) line: 526 SynchronizationImple.afterCompletion(int) line: 123 AtomicAction(TwoPhaseCoordinator).afterCompletion(int) line: 389 AtomicAction(TwoPhaseCoordinator).cancel() line: 116 AtomicAction(AtomicAction).cancel() line: 213 TransactionReaper.doCancellations() line: 446 ReaperWorkerThread.run() line: 91
If you are in the middle of fetching, such as in the following stack, then you will fail on the assertion:
Thread [WorkerThread#0[10.23.132.245:61252]] (Suspended) TwoPhaseLoad.initializeEntity(Object, boolean, SessionImplementor, PreLoadEvent, PostLoadEvent) line: 121 QueryLoader(Loader).initializeEntitiesAndCollections(List, Object, SessionImplementor, boolean, Object) line: 1030 QueryLoader(Loader).initializeEntitiesAndCollections(List, Object, SessionImplementor, boolean) line: 987 QueryLoader(Loader).doQuery(SessionImplementor, QueryParameters, boolean, ResultTransformer) line: 896 QueryLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor, QueryParameters, boolean, ResultTransformer) line: 298 QueryLoader(Loader).doList(SessionImplementor, QueryParameters, ResultTransformer) line: 2455 QueryLoader(Loader).doList(SessionImplementor, QueryParameters) line: 2440 QueryLoader(Loader).listIgnoreQueryCache(SessionImplementor, QueryParameters) line: 2316 QueryLoader(Loader).list(SessionImplementor, QueryParameters, Set, Type[]) line: 2311 QueryLoader.list(SessionImplementor, QueryParameters) line: 414 QueryTranslatorImpl.list(SessionImplementor, QueryParameters) line: 364 HQLQueryPlan.performList(QueryParameters, SessionImplementor) line: 196 SessionImpl.list(String, QueryParameters) line: 1192 QueryImpl.list() line: 102 QueryImpl.getResultList() line: 94 ...
This unsafe access to the session occurs because the fetch is happening and, in parallel, the reaper clears the session.
An appropriate implementation would just position a flag using some synchronized code in the session from the reaper, and let the thread that uses the session consult the flag and take appropriate actions, such as clearing the session and throwing an exception with a clear message.