Registering transaction synchronization during time of beforeCompletion
ochaloup Sep 26, 2018 8:53 AMHello,
I've been investigating the issue based on the query on the stackoverflow hibernate - Defer TransactionalObserverNotifier fails with ARJUNA016082: Synchronizations are not allowed (issues filled for WELD/CDI - WELD-2444, CDI-724). I started looking at it from Narayana perspective.
The issue WELD-2444 is nicely described and I created the reproducer for the WFLY testsuite. martinkouba mentions that the behavior is correct based on the CDI specification. I wonder if it's an issue of the Narayana code. Or in case the transaction integration in WFLY.
What happens
The Hibernate synchronization (Synchronization (Java(TM) EE 7 Specification APIs) ) runs code of the method @PostPersist. Because this is the JPA synchronization it has to be specifically ordered against the JCA synchronization (based on the comment in the wildfly/TransactionSynchronizationRegistryWrapper.java at master · wildfly/wildfly · GitHub that's why there is the wildfly/JCAOrderedLastSynchronizationList.java at master · wildfly/wildfly · GitHub created). The code of the @PostPersist method drives (via CDI event) a new synchronization to be added. This flows up to the TwoPhaseCoordinator class to addSynchronization method (narayana/TwoPhaseCoordinator.java at master · jbosstm/narayana · GitHub ). There is linked issue [JBTM-608] Allow synchronization registration as long as beforeCompletion phase not finished - JBoss Issue Tracker which says that registering the synchronization on beforeCompletion should be possible. But here it isn't as the synchronization that would be added is *non* interposed (aka. it's normal synchronization). But the last executed synchronization is the JCAOrderedLastSynchronizationList which is interposed thus running as the last (after all normal *non* interposed ones). Because of that the code decline to add such synchronization as the new one is smaller when compared to the _currentRecord (narayana/SynchronizationImple.java at master · jbosstm/narayana · GitHub ). The exception is thrown and it causes what is described at the stackoverflow/in the issue.
(The newly adding synchronization would be run only afterCompletion).
Should not be permitted to add the new synchronization in way the logic in TwoPhaseCoordinator would care only about ordering but not about the fact the synchronization is interposed? I mean when interposed synchronization is already running then it won't happen that when there is added new synchronization to the 'sync' treemap that this new synchronization would be launched after that, right. I mean when interposed synchronizations are already running Narayana won't touch to the normal one again. (it seems so from my experiments too).
Do you think synchronization could be added just based on the ordering without considering the interposed flag?
Thank you for ideas
Ondra
Stacktrace at time the new (eventually failing synchronization) should be registered
Thread [default task-1] (Suspended) owns: Object (id=562) AtomicAction(TwoPhaseCoordinator).addSynchronization(SynchronizationRecord) line: 168 TransactionImple.registerSynchronizationImple(SynchronizationImple) line: 388 TransactionImple.registerSynchronization(Synchronization) line: 377 LocalTransaction.registerSynchronization(Synchronization) line: 179 WeldTransactionServices.registerSynchronization(Synchronization) line: 97 TransactionalObserverNotifier.notifyTransactionObservers(List<ObserverMethod<? super T>>, T, EventMetadata, ObserverExceptionHandler) line: 86 TransactionalObserverNotifier(ObserverNotifier).notify(ResolvedObservers<T>, T, EventMetadata) line: 274 EventImpl<T>.fire(T) line: 96 TestPersistEntityListener.onEntityPostPerist(Object) line: 42 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ListenerCallback.performCallback(Object) line: 35 CallbackRegistryImpl.callback(Callback[], Object) line: 97 CallbackRegistryImpl.postCreate(Object) line: 66 PostInsertEventListenerStandardImpl.onPostInsert(PostInsertEvent) line: 31 EntityInsertAction.postInsert() line: 168 EntityInsertAction.execute() line: 135 ActionQueue.executeActions(ExecutableList<E>) line: 604 ActionQueue.executeActions() line: 478 DefaultFlushEventListener(AbstractFlushingEventListener).performExecutions(EventSource) line: 356 DefaultFlushEventListener.onFlush(FlushEvent) line: 39 SessionImpl.doFlush() line: 1454 SessionImpl.managedFlush() line: 511 SessionImpl.flushBeforeTransactionCompletion() line: 3283 SessionImpl.beforeTransactionCompletion() line: 2479 JdbcCoordinatorImpl.beforeTransactionCompletion() line: 473 JtaTransactionCoordinatorImpl.beforeCompletion() line: 352 SynchronizationCallbackCoordinatorTrackingImpl(SynchronizationCallbackCoordinatorNonTrackingImpl).beforeCompletion() line: 47 RegisteredSynchronization.beforeCompletion() line: 37 JCAOrderedLastSynchronizationList.beforeCompletion() line: 113 844757553.accept(Object) line: not available 22702947.accept(Object, Object) line: not available LocalTransaction(AbstractTransaction).performConsumer(ExceptionBiConsumer<T,U,E>, T, U) line: 236 LocalTransaction(AbstractTransaction).performConsumer(ExceptionConsumer<T,E>, T) line: 247 AbstractTransaction$AssociatingSynchronization.beforeCompletion() line: 292 SynchronizationImple.beforeCompletion() line: 76 AtomicAction(TwoPhaseCoordinator).beforeCompletion() line: 360 AtomicAction(TwoPhaseCoordinator).end(boolean) line: 91 AtomicAction(AtomicAction).commit(boolean) line: 162 TransactionImple.commitAndDisassociate() line: 1288 TransactionManagerImple(BaseTransaction).commit() line: 126 TransactionManagerDelegate(BaseTransactionManagerDelegate).commit() line: 89 LocalTransaction.commitAndDissociate() line: 77 ContextTransactionManager.commit() line: 71 TransactionalInterceptorRequired(TransactionalInterceptorBase).endTransaction(TransactionManager, Transaction) line: 232 TransactionalInterceptorRequired(TransactionalInterceptorBase).invokeInOurTx(InvocationContext, TransactionManager) line: 178 TransactionalInterceptorRequired.doIntercept(TransactionManager, Transaction, InvocationContext) line: 53 TransactionalInterceptorRequired(TransactionalInterceptorBase).intercept(InvocationContext) line: 88 TransactionalInterceptorRequired.intercept(InvocationContext) line: 47 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(InvocationContext) line: 73 InterceptorMethodHandler.executeAroundInvoke(Object, Method, Method, Object[], InterceptorMethodHandler$CachedInterceptionChain, InterceptionDecorationContext$Stack) line: 84 InterceptorMethodHandler.executeInterception(Object, Method, Method, Object[], InterceptionType, InterceptionDecorationContext$Stack) line: 72 InterceptorMethodHandler.invoke(InterceptionDecorationContext$Stack, Object, Method, Method, Object[]) line: 56 CombinedInterceptorAndDecoratorStackMethodHandler.invoke(InterceptionDecorationContext$Stack, Object, Method, Method, Object[], boolean, boolean) line: 79 CombinedInterceptorAndDecoratorStackMethodHandler.invoke(InterceptionDecorationContext$Stack, Object, Method, Method, Object[]) line: 68 TestEntityManipulatorBean$Proxy$_$$_WeldSubclass.createTestEntity(String) line: not available TransactionalObserverJPAListenerTestCase.commitObserved() line: 80 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 FrameworkMethod$1.runReflectiveCall() line: 50 FrameworkMethod$1(ReflectiveCallable).run() line: 12 FrameworkMethod.invokeExplosively(Object, Object...) line: 47 Arquillian$8$1.invoke(Object...) line: 379 LocalTestExecuter.execute(LocalExecutionEvent) line: 60 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.invokeObservers() line: 103 EventContextImpl<T>.proceed() line: 85 ManagerImpl.fire(T, NonManagedObserver<T>) line: 143 ManagerImpl.fire(Object) line: 114 EventImpl<T>.fire(T) line: 67 ContainerTestExecuter.execute(Test) line: 38 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.invokeObservers() line: 103 EventContextImpl<T>.proceed() line: 85 TestContextHandler.createTestContext(EventContext<TestEvent>) line: 130 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.proceed() line: 92 TestContextHandler.createClassContext(EventContext<ClassEvent>) line: 92 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.proceed() line: 92 TestContextHandler.createSuiteContext(EventContext<SuiteEvent>) line: 73 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.proceed() line: 92 ManagerImpl.fire(T, NonManagedObserver<T>) line: 143 EventTestRunnerAdaptor.test(TestMethodExecutor) line: 136 Arquillian$8.evaluate() line: 372 Arquillian$4.evaluate() line: 246 Arquillian.multiExecute(Statement...) line: 431 Arquillian.access$200(Arquillian, Statement[]) line: 55 Arquillian$5.evaluate() line: 260 Arquillian$7$1.invoke() line: 324 BeforeLifecycleEventExecuter.on(BeforeTestLifecycleEvent) line: 35 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.invokeObservers() line: 103 EventContextImpl<T>.proceed() line: 85 TestContextHandler.createTestContext(EventContext<TestEvent>) line: 130 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 498 ObserverImpl.invoke(Manager, Object) line: 96 EventContextImpl<T>.proceed() line: 92 TestContextHandler.createClassContext(EventContext<ClassEvent>) line: 92 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]