Scenario:
The AS is configured to use a JDBC storage for EJB timers (using the H2 ExampleDS is sufficient for reproduction)
There is a persistent calendar timer going off every few seconds
The 'trigger' management operation is called on that timer
The timer goes off one time extra, as expected
The 'suspend' management operation is called on that timer
After a few seconds, the 'activate' operation is called on that timer
The timer will never go off again (not even by invoking 'trigger' operation)
The cause:
Each time the timer goes off, a new java.util.TimerTask is scheduled to handle the next alarm
When the 'trigger' operation is invoked, the TimerServiceImpl computes the timestamp of the next alarm and schedules a new TimerTask
The problem is that this new TimerTask overwrites the one currently registered in the TimerServiceImpl, because the TimerServiceImpl assigns TimerTasks to Timers using a map: https://github.com/wildfly/wildfly/blob/10.0.0.Alpha1/ejb3/src/main/java/org/jboss/as/ejb3/timerservice/TimerServiceImpl.java#L905 - the TimerServiceImpl loses the reference to the previously registered TimerTask and doesn't cancel it
From now on, each new alarm registers the next TimerTask, but makes TimerServiceImpl forget the previous one
After some time, when the 'suspend' operation is called, the TimerServiceImpl cancels its stored TimerTask. However, it doesn't know that there is another scheduled elsewhere
The TimerTask goes off during the time when the timer is suspended.
DatabaseTimerPersistence checks whether the TimerTask can run and sets the Timer's state to IN_TIMEOUT if yes (https://github.com/wildfly/wildfly/blob/10.0.0.Alpha1/ejb3/src/main/java/org/jboss/as/ejb3/timerservice/persistence/database/DatabaseTimerPersistence.java#L395). However, this is done BEFORE checking whether the timer is suspended. DatabaseTimerPersistence commits this change. Then comes the mentioned check (https://github.com/wildfly/wildfly/blob/10.0.0.Alpha1/ejb3/src/main/java/org/jboss/as/ejb3/timerservice/task/TimerTask.java#L141) and it fails, so the Timer will not go off, but the Timer state remains IN_TIMEOUT forever!
With every next invocation, as the timer's state remains IN_TIMEOUT, all invocations are skipped.
- is cloned by
-
WFLY-4657 EJB timer blocked after trigger+suspend+activate operations and JDBC storage
- Closed