7

I have an anonymous Apex scheduled job that runs every three hours on the hour:

String cron = '0 00 07,10,13,16,19,22 * * ? 2015';  
MyScheduledDispatcher sd = new MyScheduledDispatcher();  
System.schedule('My Job 07-10-13-16-19-22h everyDay 2015',cron,sd);

Above job runs under user: automatedJobUser, System Admin profile

When I deploy via Changeset new code that is invoked by the schedulable class MyScheduledDispatcher (in my use case, a batchable class), I observe the following:

  1. The new code is ignored on all future scheduled runs (!?!).
  2. The new code is executed if I run the schedule under a different user (that is, launch a new schedule).
  3. The new code is executed if I cancel the job and reschedule under automatedJobuser

OBSERVATION: The scheduled job is bound to the last version of the code prior to creation of the schedule. Updating the code via a changeset has no effect

Q1 - Is this because I'm using the Appleman pattern that doesn't have a compile-time reference to the changed-by-deployment batchable class - and SFDC doesn't think a runtime recompile is required?

Q2 - Is there a better pattern to use given the code below so I can deploy at will and know that the next scheduled job instance will use the updated code?

I have Deployment options set as: enter image description here

Yes, I can read and note Enabling this option may cause Apex jobs to fail but I interpreted this as "code would be swapped in in the middle of a series of batches and results would be unpredictable"

It makes no sense to me that in one context, SFDC is executing prior versions of the Apex code for running schedules while in all other transactions, the new version of the code is used.

Schedulable class defines an interface and execute() follows the Appleman pattern for decoupling the batchable from the schedulable:

public MyScheduledDispatcher() {
    MyParms__c  p   = [select id, query__c, scope_Size__c from MyParms__c][0];      
}
public Interface IScheduleDispatched  { 
    void execute(SchedulableContext sc);
}
public void execute(SchedulableContext sc) {
    Type schedHandlerType = Type.forName('MyBatchable'); // get Type for the desired ScheduleHandler class - this is the decoupling trick
    if(schedHandlerType != null) {
        IScheduleDispatched obj = (IScheduleDispatched)schedHandlerType.NewInstance(); // create instance of the ScheduleHandler but cast as interface IScheduleDispatched
        obj.execute(sc);                                                                    // invoke the execute() method on the interface object - which will start the batch 
    } 
}

Batchable class (the one that was changed by deployment) looks like:

public with sharing class MyBatchable 
        implements  Database.Batchable<Opportunity>, Database.Stateful, Database.AllowsCallouts, 
                    Iterable<Opportunity>, Iterator<Opportunity>,
                    MySchedulable.IScheduleDispatched {
 public MyBatchable() { // do stuff }

 public void execute(SchedulableContext sc) { // starts batch
    ID  batchProcessId  =  Database.executeBatch(this,this.scopeSize);
 }
 public Iterator<Opportunity> iterator() {return this;}
 public Boolean hasNext() { // do stuff }
 public Opportunity next() { // do stuff }
 public Iterable<Opportunity> start(Database.BatchableContext BC){
     // do stuff }
 public void execute(Database.BatchableContext bc, List<sObject> scope) {
     // do useful stuff }
 public void finish(Database.BatchableContext bc) { // do stuff }

Update:

This is going to take some more experimentation with alternatives -- otherwise I'm going to have to delete and restart the schedule on every code change (not good, as I'll forget something some day)

  • If you upload a new version, and use Compile All Classes afterwards, does it still use the old version, or does that cause it to use new version? – sfdcfox Aug 6 '15 at 4:59
  • @sfdcfox i'll try this but doesn't a PROD deploy recompile all classes in order to run the testmethods? – cropredy Aug 6 '15 at 22:21
  • My understanding is that it does not. Go check Apex Classes, and create a view for "Is Valid equals false" to see how many classes you don't have compiled. Each class is stored in a cache and only compiled when using the Compile all classes feature or when deploying that specific class. – sfdcfox Aug 6 '15 at 22:35
  • 1
    @Eric don't have an answer as the org I built this on has been retired. I do know that a schedulable's constructor is not reinvoked on each job execution if on a recurring schedule. – cropredy Feb 1 '17 at 3:23
  • 1
    @sfdcFanBoy - It was at the time yes. not sure about now – Eric Nov 6 '17 at 5:49
0

In case you are considering the Force.com Migration tool for your deployments, you can have a look at ForceFlow. It's a library I built on top of the metadata & tooling APIs to enhance automation around deployments. One of the features it has is clearing and resetting scheduled jobs.

  • you're welcome :) – Lorenzo Nov 23 '15 at 10:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.