3 Replies Latest reply on Mar 13, 2009 12:38 PM by kapitanpetko

    using components.xml to schedule Quartz job in cluster

    admin.admin.email.tld

      The scheduling using EJB3 timer serive or Quartz for jobs is working fine for an interval-based timer (use case is to query AD every x seconds/minutes and cache the data in an instance variable).


      The question is what happens when I use the following in components.xml to schedule/create a job when the app is deployed in a clustered JBoss environment?


      I do not have access to a JBoss cluster but we will be deploying to a prod 2-node cluster.


      My concern is that the job will be created twice (or n times per n nodes in the cluster).  By default, Quartz uses in-memory storage.  For
      clustered   
      mode, it is required to use JDBCJobStore for Quartz.  EJB timer does not support clusters.


      Any tips would be appreciated.  thx.



      <event type="org.jboss.seam.postInitialization">   
              <action execute="#{controller.scheduleTimer}"/>
         </event>
      
         <!-- Install the QuartzDispatcher -->
         <async:quartz-dispatcher/>  



      controller POJO:



      @Name("controller")
      @AutoCreate
      public class ScheduleController { 
         
          @In ScheduleProcessor processor;    
          @Logger Log log;        
           private Map<String,List<UserItem>> map;     
           private String distinguishedNameShims;     
           private String distinguishedNameItJava;     
           private Timer timer;     
           private QuartzTriggerHandle quartzTriggerHandle;
           
           //test method from button in JSF
          public void scheduleTimer()
          {                           
            ...
          }
      }




      application-scoped POJO:



      @Name("processor")
      @AutoCreate
      @Scope(ScopeType.APPLICATION)
      public class ScheduleProcessor { 
      ...
      }



        • 1. Re: using components.xml to schedule Quartz job in cluster

          We handle this manually in our 12 node cluster.  ie:  Each node has a servername passed in at startup via the -D flag and then we use that name plus name of the task to query the database to see if the job should be running on that node.


          It would be nice if there were a built in way to handle this, but I haven't seen one yet.


          Some code to illustrate in case my description wasn't clear.  The SiteGlobal entity is a name/value pair we store in the db for config.


          SiteGlobal globalValue = SiteGlobal.retrieveByName(discussionSession, (serverName==null?"":serverName+ ".QUARTZ_ENABLED"));
          if(globalValue==null)
          {
               System.out.println("Quartz not approved in the SiteGlobal table to run:  siteGlobal: "+serverName+ ".QUARTZ_ENABLED");
               enabled = false;
          }
          else
          {
               enabled = globalValue.getValue().equalsIgnoreCase("ON");
          }
          
          if (enabled) { 
               apacheLogFollower.lookForNewData(5000L, 60000L);
          
          


          • 2. Re: using components.xml to schedule Quartz job in cluster
            marius.oancea
            I think this is not the idea of clustering. All the nodes has to have the chance to run the job.


            I have a similar architecture:
            @Observer("org.jboss.seam.postInitialization")
            public String callScheduler() {
            ...
              QuartzTriggerHandle handleHour = processor.scheduleCollector(new Date(), "0 0/30 * * * ?");

            }

            and:

            @Asynchronous
            @Transactional
            public QuartzTriggerHandle scheduleCollector
                    (@Expiration Date when, @IntervalCron String cron) {

               // do something at the scheduled time
            }


            My seam.quartz.property is:
            #============================================================================
            # Configure Main Scheduler Properties 
            #============================================================================
            org.quartz.scheduler.instanceName = DefaultQuartzScheduler
            org.quartz.scheduler.instanceId = AUTO
            org.quartz.scheduler.rmi.export = false
            org.quartz.scheduler.rmi.proxy = false
            org.quartz.scheduler.wrapJobExecutionInUserTransaction = false



            #============================================================================
            # Configure ThreadPool 
            #============================================================================

            org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
            org.quartz.threadPool.threadCount = 10
            org.quartz.threadPool.threadPriority = 5
            org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

            #============================================================================
            # Configure JobStore 
            #============================================================================

            org.quartz.jobStore.misfireThreshold = 60000
            org.quartz.jobStore.isClustered = true
            org.quartz.jobStore.clusterCheckinInterval = 15000

            org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
            org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.DB2v8Delegate

            org.quartz.jobStore.dataSource = QUARTZ
            org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX
            org.quartz.jobStore.tablePrefix = QRTZ_
            org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ? FOR UPDATE

            org.quartz.dataSource.QUARTZ.jndiURL = java:/emailcollectorDatasource
            org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:/emailcollectorDatasource




            There is no classpath problem on seam2.1.1 but the jobs are executed on all the nodes of the cluster.


            Any idea why?



            • 3. Re: using components.xml to schedule Quartz job in cluster
              kapitanpetko

              Marius Oancea wrote on Mar 13, 2009 11:11:

              @Observer("org.jboss.seam.postInitialization")
              public String callScheduler() {
              ...
                QuartzTriggerHandle handleHour = processor.scheduleCollector(new Date(), "0 0/30 * * * ?");
              
              }




              There is no classpath problem on seam2.1.1 but the jobs are executed on all the nodes of the cluster.


              Any idea why?



              'postInitialization' is raised for each app instance (at each node), so your job is probably scheduled multiple times. You will have to add some code to check if the job is already scheduled and skip scheduleCollector() if
              it is. You will have to use the Quartz API for that (Scheduler, Trigger and friends).


              Btw, I recommend using the Quartz API directly for scheduling as well, especially since you are running in a
              cluster. QuartzTriggerHandle is practically useless: there is no way to get the job/trigger name, you get automatically generated names (UIDs) that make debugging/support quite hard, etc. In short, the native Quartz
              API is way more flexible. Seam Quartz wrappers work well for one shot jobs (async methods and stuff) but for anything more than that you are better off with native Quartz.