Creating clustered EJB 3 Timers

Datetime:2016-08-23 01:59:23          Topic: EJB  DataBase           Share

In this tutorial we will demonstrate how to configure a Timer EJB 3 Service on a cluster of servers. You will need WildFly 8 application server and a Database supporting READ_COMMITTED or SERIALIZABLE isolation level.

Let's start from a simple EJB 3 Timer which is scheduled to be executed every 10 seconds:

@Singleton
@Startup
public class UserRegistry {
 
    @Schedule(hour = "*", minute = "*", second = "*/10", info ="Every minute timer",persistent=true)
    public void printDate() {	 
		System.out.println(" It is " + new java.util.Date().toString());
    }
 
}

Plain and easy, however the problem arises when you try to deploy the EJB 3 timer on a cluster. if you try to do that, you will see that the timer is triggered once on every node of the cluster:

Clustered EJB 3 Timer Service

Since WildFly 8 it is however possible to bind the EJB 3 Timer service to a Database storage, using an isolation mode like SERIALIZABLE which prevents concurrent change by multiple nodes of the cluster. Let's start at first to define a Datasource which is fit for our purpose:

<datasource jta="true" jndi-name="java:/PostgreSQL" pool-name="pgre" enabled="true" use-ccm="true">
	<connection-url>jdbc:postgresql://localhost/postgres</connection-url>
	<transaction-isolation>TRANSACTION_SERIALIZABLE</transaction-isolation>
	<driver>postgresql</driver>
	<security>
		<user-name>postgres</user-name>
		<password>postgres</password>
	</security>
	<validation>
		<validate-on-match>false</validate-on-match>
		<background-validation>false</background-validation>
	</validation>
	<timeout>
		<set-tx-query-timeout>false</set-tx-query-timeout>
		<blocking-timeout-millis>0</blocking-timeout-millis>
		<idle-timeout-minutes>0</idle-timeout-minutes>
		<query-timeout>0</query-timeout>
		<use-try-lock>0</use-try-lock>
		<allocation-retry>0</allocation-retry>
		<allocation-retry-wait-millis>0</allocation-retry-wait-millis>
	</timeout>
	<statement>
		<share-prepared-statements>false</share-prepared-statements>
	</statement>
</datasource>
<drivers>
		 <driver name="postgresql" module="com.postgresql"/>
</drivers>

Once done with the Datasource we will configure the EJB 3 timer service to use a datasource clustered store, related to our PostgreSQL Datasource:

<timer-service thread-pool-name="default" default-data-store="clustered-store">
   <data-stores>
        <database-data-store name="clustered-store" datasource-jndi-name="java:/PostgreSQL" database="postgresql" partition="timer"/>
   </data-stores>
</timer-service>

Please note the partition property which allows breaking a large cluster up into several smaller clusters, in order to improve the performance.

Finally we need some patching to the scripts used to creare tables and insert data in it. By default a general timer-sql.properties is contained into the folder JBOSS_HOME/modules/system/layers/base/org/jboss/as/ejb3/main/timers . Obviously this is not fit for every database so we will perform some changes to adapt it to Postgre SQL database:

create-table.postgresql=create table jboss_ejb_timer (id varchar primary key not null, timed_object_id varchar not null, initial_date timestamp, repeat_interval bigint, next_date timestamp, previous_run timestamp, primary_key varchar, info varchar, timer_state varchar, schedule_expr_second varchar, schedule_expr_minute varchar, schedule_expr_hour varchar,schedule_expr_day_of_week varchar, schedule_expr_day_of_month varchar, schedule_expr_month varchar, schedule_expr_year varchar, schedule_expr_start_date varchar, schedule_expr_end_date varchar, schedule_expr_timezone varchar, auto_timer boolean, timeout_method_name varchar, timeout_method_declaring_class varchar, timeout_method_descriptor varchar, calendar_timer boolean, partition varchar not null);
create-timer.postgresql=insert into jboss_ejb_timer (id, timed_object_id, initial_date, repeat_interval, next_date, previous_run, primary_key, info, timer_state, schedule_expr_second, schedule_expr_minute, schedule_expr_hour, schedule_expr_day_of_week, schedule_expr_day_of_month, schedule_expr_month, schedule_expr_year, schedule_expr_start_date, schedule_expr_end_date, schedule_expr_timezone, auto_timer, timeout_method_name, timeout_method_declaring_class, timeout_method_descriptor, calendar_timer, partition) values ((?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?));
update-timer.postgresql=update jboss_ejb_timer set next_date=(?), previous_run=(?), timer_state=(?) where timed_object_id=(?) and id=(?) and partition=(?);
delete-timer.postgresql=delete from jboss_ejb_timer where timed_object_id=(?) and id=(?) and partition=(?);
load-all-timers.postgresql=select id, timed_object_id, initial_date, repeat_interval, next_date, previous_run, primary_key, info, timer_state, schedule_expr_second, schedule_expr_minute, schedule_expr_hour, schedule_expr_day_of_week, schedule_expr_day_of_month, schedule_expr_month, schedule_expr_year, schedule_expr_start_date, schedule_expr_end_date, schedule_expr_timezone, auto_timer, timeout_method_name, timeout_method_declaring_class, timeout_method_descriptor, calendar_timer from jboss_ejb_timer where timed_object_id=(?) and partition=(?);
load-timer.postgresql=select id, timed_object_id, initial_date, repeat_interval, next_date, previous_run, primary_key, info, timer_state, schedule_expr_second, schedule_expr_minute, schedule_expr_hour, schedule_expr_day_of_week, schedule_expr_day_of_month, schedule_expr_month, schedule_expr_year, schedule_expr_start_date, schedule_expr_end_date, schedule_expr_timezone, auto_timer, timeout_method_name, timeout_method_declaring_class, timeout_method_descriptor, calendar_timer from jboss_ejb_timer where timed_object_id=(?) and id=(?) and partition=(?);

Fine. Now start up the application server and deploy the EJB 3 Timer to a cluster. You will see that now the timer is being triggered just on ONE node of the cluster, hence guaranteeing a single execution of the Timer:

References:

https://docs.jboss.org/author/display/WFLY9/EJB3+Clustered+Database+Timers





About List