Daily cron jobs with Apex

To date, there has been no way to run code that manipulates Salesforce.com on a predetermined schedule, without having an external server and some sort of scheduler. Lots of people are running these kinds of scripts, but the folks I work with don’t have those kinds of resources. One of the things I love about on-demand is that I don’t have to keep servers running, so I shied away from writing these kinds of scripts.

In playing with Apex, I came up with a way to run Apex scripts every day, with no user intervention. It relies on two things: time-based workflow and Apex triggers. Here’s a run down of how it works.

I created a custom object called Batch Script and then created on instance of that object.

batch_script.png

It’s got two fields that matter: a last run date field and a run checkbox. The next step is to create a timebased workflow on the object.

cron_field_update1.png

The workflow kicks off when the last run date is one day ago. When this state is reached, the run checkbox is updated.

Now, and Apex trigger fires on the update of the Batch Script object. Here’s the code:

trigger TestScript on Batch_Script__c bulk (after update) {
//if checkbox is checked, run the script
  for (Batch_Script__c bs : Trigger.new) {
    if (bs.Test_Script_Run__c == true) {
      //run whatever APEX code you want
      Batch_Script__c ourBS = new Batch_Script__c (
        Id = bs.Id,
        Test_Script_Run__c=false,
        Test_Script_Last_Run_Date__c=system.today()
      );
      update ourBS;

    }
  }
}

This trigger runs arbitrary Apex code if the run checkbox is checked. After running the code, the run checkbox is unchecked and the last run date is set to today. Tomorrow, this will happen all over again–the code will be run and the object will be returned to a state where it will fire again in 24 hours.

Do you have more control with a cron job running on an external server? Sure. Is this a kludge? Absolutely. But if all you want to do is run daily jobs that use Apex to work with your data, you can use this method and save the headache of outside servers.

These new pieces of functionality are making things possible that weren’t possible before. That’s a great sign for the future of the platform–as long as they make more things possible, the platform will continue to get more powerful.

18 Responses to “Daily cron jobs with Apex”

  1. Ron Hess Says:

    Brilliant !

  2. Steve Says:

    Thanks Ron!

  3. Perspectives on Salesforce.com » Schedule your Apex logic Says:

    […] Steve over at gokubi.com has been doing a lot with Apex Code recently. He has a clever post about writing Apex code triggers and having them run on a schedule. It’s a good post to get the wheels turning with regards to what you can now do on the platform. […]

  4. Need Daily Jobs Run? Apex Opens the Possibilities « My Favorite Top Gear Says:

    […] Steve’s blog […]

  5. Need Daily Code to Run? Apex Opens Up the Possibilities « Salesforce.com News @ Lawson Says:

    […] Steve’s blog […]

  6. Mike Leach Says:

    Very nice. I’m looking to call external web services using a similar approach. Anyone done this with outbound messages?

  7. Raja Says:

    While it is indeed an ingenious solution, I must let you aware of some of the governor limits for time-based workflow.
    Because Time-based Workflow runs in a multi-tenant environment, the Time-based Workflow runtime engine strictly enforces the limits on the number of time triggers which can per processed by an organization to ensure that the shared resources are not monopolized. These limits, or governors, track and enforce the statistics outlined in the table below. If an organization ever exceeds a limit, the time-based workflow governor will stop processing and will defer all processing to the next window.

    Edition Limit Window
    Unlimited Edition 100,000 time triggers per org Day
    Enterprise Edition 20,000 time triggers per org Day

    So, when you are using this feature, please keep in mind the governor limits when processing large volumes of data.

  8. Steve Says:

    Thanks for the info Raja. My thoughts on using this technique is to run just a handful of scripts each day, so it’s nice to see the limits that high.

  9. Marc B Says:

    Steve,

    This is a great way to create a SF cron job. I envision this as a once or twice a day script that does something to my Salesforce objects, or to my external databases which sync with SF. Super cool. I don’t plan on hitting the 100,000 limit. Thanks again for being on the cutting edge.

    Marc

  10. Steve Says:

    With this method, I can run a job once a day. I don’t think there is any way to run a job on a schedule other than daily increments because of the way time-based workflow works.

    Maybe Salesforce.com will make this all easier by putting in a simple scheduling interface for Apex blocks. It would add to the app dev capabilities of the platform, that’s for sure.

  11. Glenn Weinstein Says:

    A kludge to run more than 1x/day would be to change “Test Script Last Run Date” from a Date to a DateTime, then create multiple “Batch Script” objects with staggered times. In my testing, time-based workflows will execute 24 hours after the specific DateTime, although there is typically a 5-10 minute lag between the “Evaluation Date” in the workflow queue (when the workflow should, theoretically, fire) and when it actually executes.

    For example: if you want a batch job to fire every 2 hours, you could create 12 Batch Script objects, each with a Test Script Run Date 2 hours later than the last. The practical limit would be around 10-15 minute intervals (due to the lag described above).

  12. Steve Says:

    Thanks for the tip Glenn. I haven’t played with execution times of time-based workflow and was a bit curious. Nice to hear from someone who’s looked into it a bit.

  13. Kingsley Says:

    Fantastic!

  14. Raja Says:

    Hello Folks,
    Wanted to give you an update on the limits. Please disregard the earlier numbers.
    The limits are as follows:
    • Unlimited Edition=20,000 time triggers per org
    • Enterprise Edition=10,000 time triggers per org

    For the latest numbers, please refer to the FAQ, which i will be posting soon on Time-based workflow feature detail page on successforce.com website.

  15. Harm Korten Says:

    Very nice ! Like with many issues, which can only be solved with work arounds or external resources at the moment, APEX will do the trick. Very nice solution to run batches. Our company has developped quite a lot of batchtasks for customers, all hosted on external servers. I think your APEX solution will make running batches even easier in the future.

  16. Chris Says:

    Love it - great work, Steve!

  17. Scott Morrison Says:

    Nice work! I’ve taken the concept a bit further to allow time based hourly and daily scripts. http://scott.morrisonlive.ca/sfSimpleBlog/show/stripped_title/time-based-batch-scripts-in-salesforce

  18. Ron Says:

    I’m curious, does this approach still work? I’ve been playing with the same type of approach and have run into a roadblock (making me think Salesforce may have made changes to the way they process triggers and workflow):

    The general flow goes like this (I’ve simplified the debug output for brevity sake) …

    BatchObject is modified, RunBatchJob=true.

    Beginning triggerX on BatchObject (After Update)
    Batch job runs and sets RunBatchJob=false

    Beginning triggerX on BatchObject (After Update)
    This time RunBatchJob=false, so no action taken.
    Ending triggerX

    Beginning Workflow Evaluation
    RunBatchJob=false so criteria evaluates to true and time-based action is queued.
    Ending workflow evaluation

    Ending triggerX

    Beginning Workflow Evaluation
    RunBatchJob=true (!! apparently the original version of BatchObject) so criteria evaluates to false and time-based action is removed from queue
    Ending workflow evaluation

    How do I stop the Workflow Evaluation at the end from occuring and undoing my work? I’ve tried setting separate flags for “run apex” and “run scheduler”, and I’ve run the trigger before and after update, but before the trigger/workflow cycle can complete, my trigger has to update a field used in the workflow rule and this problem re-appears.

Leave a Reply