EJB timer blocked after trigger+suspend+activate operations and JDBC
storage
----------------------------------------------------------------------------
Key: WFLY-4657
URL:
https://issues.jboss.org/browse/WFLY-4657
Project: WildFly
Issue Type: Bug
Components: EJB
Affects Versions: 10.0.0.Alpha1
Reporter: Jan Martiska
Assignee: Stuart Douglas
Fix For: 10.0.0.Alpha2
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/...
- 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/...).
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/...)
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.