To enable Quartz.NET internal logging, add the following to Quartz.Server.exe.config -
1) Use the following <common> config section
<common>
<logging>
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net1213">
<arg key="configType" value="INLINE"/>
</factoryAdapter>
</logging>
</common>
2) <appender name="QuartzInternalLog" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="Trace/QuartzInternalLogFile_%date{yyyyMMdd}.txt"/>
<appendToFile value="true"/>
<maximumFileSize value="1024KB"/>
<maxSizeRollBackups value="1000" />
<rollingStyle value="Size"/>
<datePattern value="yyyyMMdd" />
<staticLogFileName value="true" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss} [%t] %-5p %c - %m%n"/>
</layout>
</appender>
3) <logger name="Quartz">
<level value="DEBUG" />
<appender-ref ref="QuartzInternalLog" />
</logger>
(NO NEED TO ADD QUARTZINTERNALLOG TO ROOT LEVEL LOGGER. THIS IS TO KEEP QUARTZ INTERNAL LOG SEPARATE).
To enable clustering in Quartz.NET, add the following to Quartz.Server.exe.config -
<quartz>
<add key="quartz.checkConfiguration" value="false"/>
<add key="quartz.server.serviceName" value="ABC-Project-QuartzServer-UAT"/>
<add key="quartz.server.serviceDisplayName" value="ABC-Project-QuartzServer-UAT"/>
<add key="quartz.server.serviceDescription" value="ABC-Project-QuartzServer-UAT"/>
<add key="quartz.scheduler.instanceName" value="ABCProjectScheduler"/>
<add key="quartz.scheduler.instanceId" value = "AUTO"/>
<add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz"/>
<add key="quartz.threadPool.threadCount" value="20"/>
<add key="quartz.threadPool.threadPriority" value="Normal"/>
<add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz"/>
<add key="quartz.plugin.xml.fileNames" value="~/quartz_jobs.xml"/>
<add key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz"/>
<add key="quartz.scheduler.exporter.port" value="555"/>
<add key="quartz.scheduler.exporter.bindName" value="QuartzScheduler"/>
<add key="quartz.scheduler.exporter.channelType" value="tcp"/>
<add key="quartz.scheduler.exporter.channelName" value="httpQuartz"/>
<add key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"/>
<add key="quartz.jobStore.dataSource" value="default"/>
<add key="quartz.jobStore.tablePrefix" value="QRTZ_"/>
<add key="quartz.jobStore.clustered" value="true"/>
<add key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz"/>
<add key="quartz.dataSource.default.provider" value="SqlServer-20"/>
<add key="quartz.jobStore.useProperties" value="true"/>
<add key="quartz.jobStore.misfireThreshold" value="600000" />
<add key="quartz.dataSource.default.connectionStringName" value="QuartzStore"/>
<add key="quartz.dataSource.default.connectionString" value="Data Source=MyServer;Initial Catalog=ABC_Project_quartz_scheduler;User ID=MyUser;Password=MyPass"/>
</quartz>
NOTE THE ABOVE SETTINGS CAN ALSO BE DONE IN Quartz.Config file but Quartz.Server.exe.config WILL TAKE THE PRECEDENCE. I personally like keeping all the settings in one place i.e. Quartz.Server.exe.config.
To prevent concurrent execution of the job, do the following steps -
1) Use the following attribute – [DisallowConcurrentExecution] in the job class
<trigger>
<cron>
<name>LongRunningJobTrigger</name>
<group>CronTriggerGroup</group>
<description>CronTrigger description</description>
<job-name>LongRunningJob</job-name>
<job-group>QuartzJobLib</job-group>
<misfire-instruction>DoNothing</misfire-instruction>
<cron-expression>0 0/1 * 1/1 * ? *</cron-expression>
</cron>
</trigger>
3) Default behavior would fire new job instance in
a separate thread. We need to avoid that.
4) With the above approach, the
below output is produced – (with trigger every minute (60 sec), and each
job taking 90 seconds)
[13396] 10/15/2015 1:16:00 AM
635804487114616685 Main Job Process - Start
[13396] 10/15/2015 1:16:00 AM 635804487114616685
Internal Job Process - Start
[13396] 10/15/2015 1:17:30 AM
635804487114616685 Internal Job Process - End
[13396] 10/15/2015 1:17:30 AM
635804487114616685 Main Job Process - End
[13396] 10/15/2015 1:18:00 AM
635804487114616723 Main Job Process - Start
[13396] 10/15/2015 1:18:00 AM
635804487114616723 Internal Job Process - Start
[13396] 10/15/2015 1:19:30 AM
635804487114616723 Internal Job Process - End
[13396] 10/15/2015 1:19:30 AM
635804487114616723 Main Job Process - End
[13396] 10/15/2015 1:20:00 AM
635804487114616724 Main Job Process - Start
[13396] 10/15/2015 1:20:00 AM
635804487114616724 Internal Job Process - Start
[13396] 10/15/2015 1:21:30 AM
635804487114616724 Internal Job Process - End
[13396] 10/15/2015 1:21:30 AM
635804487114616724 Main Job Process - End
[13396] 10/15/2015 1:22:00 AM
635804487114616725 Main Job Process - Start
[13396] 10/15/2015 1:22:00 AM
635804487114616725 Internal Job Process – Start
Note from the above output
a) The new job (ID)
does not starts until the previous job is finished.
b) The new job did not
start at 1:17:30, instead it started at 1:18:00 (Because of the misfire instruction “DoNothing”)
5) Note misfire-instruction property is available only with CronTrigger (not with SimpleTrigger). My suggestion is to always use CronTrigger instead of SimpleTrigger.