FIX: Custom timer job triggered multiple times

Scenario:
In a SharePoint farm, your custom timer job fires on each server of your farm. You only want it to fire once.

Fix:
You will find countless sites offering solutions for this problems and most of them do not work.

You are probably implementing a class inheriting from SPJobDefinition:

   class TimerJob : SPJobDefinition  { ... }  

SPJobDefinition has two public constructors (and one reserved for internal use):
     // Parameters:  
     //  name: Specifies the name of the job definition.  
     //  service: Specifies an Microsoft.SharePoint.Administration.SPService object instance.  
     //  server: Specifies an Microsoft.SharePoint.Administration.SPServer object instance.  
     //  lockType: Specifies an enumeration value from the Miccrosoft.SharePoint.Administration.SPJobLockType enum.  
     protected SPJobDefinition(string name, SPService service, SPServer server, SPJobLockType lockType);  

     // Parameters:  
     //  name: Specifies the name of the job definition.  
     //  webApplication: Specifies an Microsoft.SharePoint.Administration.SPWebApplication  
     //  server: Specifies an Microsoft.SharePoint.Administration.SPServer object instance.  
     //  lockType: Specifies an enumeration value from the  
 object instance.  
     protected SPJobDefinition(string name, SPWebApplication webApplication, SPServer server, SPJobLockType lockType);  
 pe);  

It would seem that setting lockType to SPJobLockType.Job solves the problem. The job should only run once per job instance. In fact, unless you also specify an application server, the job will still run on ever server in the farm, therefore multiple times.

Suggested implementation:

Timer class:
 class TimerJob : SPJobDefinition {  
     public TimerJob(SPWebApplication webApp, SPServer server)  
       : base(JobTitle, webApp, server, SPJobLockType.Job)  
     {  
       Title = JobTitle;  
     }  
 ...  
 }  

Create the timer (in a SPFeatureReceiver for example):
   public class OrbisTaskMessengerEventReceiver : SPFeatureReceiver  
   {  
     public override void FeatureActivated(SPFeatureReceiverProperties properties)  
     {  
       CreateJob(properties.Feature.Parent as SPSite);  
     }  
     //create the timer job  
     private static void CreateJob(SPSite site) {  
       SPServer server = RandomApplicationServer(site);  
       if (server != null) {  
         TimerJob job = new TimerJob(site.WebApplication, server);  
         SPDailySchedule schedule = new SPDailySchedule { BeginHour = 7, EndHour = 8 };  
         job.Schedule = schedule;  
         job.Update();  
       }  
       else {  
         throw new System.Exception("Failed to create job; cannot determine an Application server in the farm.");  
       }  
     }  
     //find a random application server in the farm  
     private static SPServer RandomApplicationServer(SPSite site) {  
       foreach (SPServer server in site.WebApplication.Farm.Servers) {  
         if (server.Role == SPServerRole.Application)  
           return server;  
       }  
       return null;  
     }  

Leave a Reply