Best practices for deploying timer jobs in multi-WFE server farm environments

I have a timer job that I want to run only once a day, for the whole farm. Like me

  • Deploy it in a multi-WFE environment? Am I running the stsadm -o deploysolution command in every WFE or just the one where I want to run it?
  • Where should I activate the function? Should it be activated only from a specific WFE?
  • What should be the value of type SPJobLockType.
+7
sharepoint sharepoint-2010 sharepoint-2007 timer-jobs
source share
1 answer

It looks like you need a farm function that installs a service that does this job. Here's how I did it (using code written by a colleague, to be honest, but it's not in SO).

Create a feature.xml file with a function event receiver.

<Feature Id="..." Title="..." Description="..." Scope="Farm" Version="1.0.0.0" Hidden="FALSE" ReceiverAssembly="XXX.FarmService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxx" ReceiverClass="XXX.FarmService.XXXFarmFeatureEventReceiver" xmlns="http://schemas.microsoft.com/sharepoint/"> </Feature> 

Inside the event receiver:

 public override void OnFeatureActivated(SPFeatureReceiverProperties properties) { try { SPFarm farm = SPFarm.Local; // Get Service, if it already exists JobService myService = null; // JobService is a subclass of SPService foreach (SPService service in farm.Services) { if (String.Compare(service.Name, JobService.XXXServiceName, true) == 0) { myService = (service as JobService); break; } } if (cegService == null) { // Create service myService = new JobService(farm); myService.Update(); // Create service instances JobServiceInstance myServiceInstance; // JobServiceInstance is a subclas of SPServiceInstance foreach (SPServer server in farm.Servers) { myServiceInstance = new JobServiceInstance(server, myService); myServiceInstance.Update(); } } // Dayly schedule SPDailySchedule schedule = new SPDailySchedule(); schedule.BeginHour = 1; schedule.EndHour = 1; schedule.BeginMinute = 0; schedule.EndMinute = 59; // Our own job; JobCheckDocDates is a subclass of SPJobDefinition JobCheckDocDates newJob = new JobCheckDocDates(cegService, null, SPJobLockType.Job); newJob.Schedule = schedule; newJob.Update(); myService.JobDefinitions.Add(newJob); myService.Update(); } catch (Exception e) { Logger.Error("[" + properties.Feature.Definition.DisplayName + "] Error during feature activation", e); } } 

This creates a service that is available on each server in the farm.

Additional information on subclasses:

 public class JobCheckDocDates: Common.BaseJob { /// <summary> /// The job name /// </summary> public static string JobName = "XXX job"; /// <summary> /// Constructor /// </summary> public JobCheckDocDates() : base() { } /// <summary> /// Constructor /// </summary> /// <param name="service"></param> /// <param name="server"></param> /// <param name="lockType"></param> public JobCheckDocDates(SPService service, SPServer server, SPJobLockType lockType) : base(JobName, service, server, lockType) { } 

...

and, of course, the Execute method.

 public class JobService : SPService { public static string XXXServiceName = "XXX Service"; public override string DisplayName { get { return XXXServiceName; } } public override string TypeName { get { return "XXX Service Type"; } } /* An empty public constructor required for serialization. */ public JobService() { } public JobService(SPFarm farm) : base(XXXServiceName, farm) { } } public class JobServiceInstance : SPServiceInstance { /// <summary> /// Eos Service Instance Name /// </summary> public static string XXXServiceInstanceName = "XXXServiceInstance"; /// <summary> /// Manage Link /// </summary> private SPActionLink _manageLink; /// <summary> /// Provision Link /// </summary> private SPActionLink _provisionLink; /// <summary> /// Unprovision Link /// </summary> private SPActionLink _unprovisionLink; /// <summary> /// Roles /// </summary> private ICollection<string> _roles; /// <summary> /// Manage Link /// </summary> public override SPActionLink ManageLink { get { if (_manageLink == null) { _manageLink = new SPActionLink(SPActionLinkType.None); } return _manageLink; } } /// <summary> /// Provision Link /// </summary> public override SPActionLink ProvisionLink { get { if (_provisionLink == null) { _provisionLink = new SPActionLink(SPActionLinkType.ObjectModel); } return _provisionLink; } } /// <summary> /// Unprovision Link /// </summary> public override SPActionLink UnprovisionLink { get { if (_unprovisionLink == null) { _unprovisionLink = new SPActionLink(SPActionLinkType.ObjectModel); } return _unprovisionLink; } } /// <summary> /// Roles /// </summary> public override ICollection<string> Roles { get { if (_roles == null) { _roles = new string[1] { "Custom" }; } return _roles; } } /// <summary> /// Empty constructor /// </summary> public JobServiceInstance() : base() { } /// <summary> /// Constructor /// </summary> /// <param name="server">The server</param> /// <param name="service">The Eos service</param> public JobServiceInstance(SPServer server, JobService service) : base(XXXServiceInstanceName, server, service) { } 

Now, in Central Administration, go to Operations / Services on the server. Select the desired server and start the service.

To answer your list of questions: 1. Deploy the solution only once, regardless of WFE. 2. Since the function has a farm scope, it must be activated in the central administrator. 3. SPJobLockType is SPJobLockType.Job

This is not exactly what you imagine, but it has the advantage of allowing you to easily choose where the task is performed, even after installing this function (for example, if the server is overloaded with other materials).

The OnFeatureActivated method can be smarter and check each server, if the service exists, and add it if necessary. But it’s easier to remove this service from each service in OnFeatureDeactivated. So, if you add new servers, deactivate and then reactivate the function.

+7
source share

All Articles