Skip navigation

**Quick check to make sure this post is worth 5 minutes of your precious work day:**

If you have ever wanted:

(1) To run long chains of Batch Apex jobs in sequence without using up scheduled Apex jobs
(2) To run batch/scheduled Apex as often as every 5 minutes every day
(3) To manage, mass-edit, mass abort, and mass-activate all your scheduled and aggregable/chained Apex jobs in one place, in DATA
(4) To avoid the pain of unscheduling and rescheduling Apex jobs when deploying

Then keep reading :)

A recent (well, maybe not so recent — is it June already???) blog post by Matt Lacey really struck a chord with me. Matt highlights one of the biggest developer woes related to using Scheduled Apex — whenever you want to deploy code that is in some way referenced by an Apex Class (or classes) that is/are scheduled, you have to unschedule all of these classes. With Spring 12, Salesforce upped the limit on the number of Scheduled Apex Classes from 10 to 25 (ha–lellujah! ha–lellujah!). However, with 15 more scheduled jobs to work with, this have-to-unschedule-before-deploying problem becomes even more of a pain.

But this isn’t the only woe developers have related to asynchronous Apex. An issue that has shown up a lot lately on the Force.com  Developer Boards and LinkedIn Groups is that of running Batch Apex jobs in sequence — run one batch job, then run another immediately afterwards. Cory Cowgill published an excellent solution for accomplishing this, and this has been the go-to method for linking Batch Apex jobs ever since. But one of the problems with his method is that it leaves a trail of scheduled jobs lying in its wake — seriously cluttering your CronTrigger table. If you want to run these batch process sequences often — e.g. kicking them off from a trigger, or running them every day (or even every hour!), you have to start “managing” your scheduled jobs more effectively.

A third issue often cited by developers is the frequency at which jobs can be run. Through the UI, a single CronTrigger can only be scheduled to run once a day. Through code, you can get down to once an hour. If you wanted to, say, run a process once every 15 minutes, you’d have to schedule the same class 4 times — using up 4/25 (16%) of your allotted scheduled jobs — and you have to manage this through Apex, not the UI.

As I mulled over these issues, I thought, there’s got to be a better way.

There is.

Enter Relax

Relax is a lightweight app I’m about to release, but before I do, I’m throwing it out for y’all to try as a public beta. (The install link is at the end of the post, but restrain yourself, we’ll get there :) )

Broadly speaking, Relax seeks to address all of these challenges, and a whole lot more.

At a high level, here are a few of the things it lets you do:

  1. Manage all your individually-scheduled Apex jobs in data records (through a Job__c object). Jobs can be mass scheduled and mass aborted, or mass exported between orgs, and then relaunched with clicks, not code.
  2.  Create and schedule ordered Batch Apex “chains” of virtually unlimited length, with each “link” in the chain kicking off the next link as soon as it finishes. And all of your chained processes are handled by, on average, ONE Scheduled Job.
  3. Schedule jobs to be run as often as EVERY 1 MINUTE. ONE. UNO.
  4. Run ad-hoc “one-off” scheduled jobs without cutting into your 25
Let’s jump in.
 
Individual vs. Aggregable
In Relax, there are 2 types of jobs: individual and aggregable. You create a separate Job__c record corresponding to each of your Individual jobs, and all of the detail about each job is stored in its record. You can then separately or mass activate / deactivate your jobs simply by flipping the Active? checkbox. Each Individual job is separately scheduled — meaning there’s a one-to-one mapping between a CronTrigger record and an Individual Job__c record. Here’s what it looks like to create an Individual Job. You simply specify the CRON schedule defining when the job should run, and choose a class to run from a drop-down of Schedulable Apex Classes.

Aggregable jobs, on the other hand, are all run as needed by the Relax Job Scheduler at arbitrary increments. For instance, you could have an aggregable job that runs once every 5 minutes that checks for new Cases created by users of your public Force.com Site, whose Site Guest User Profile does not have access to Chatter, and inserts Chatter Posts on appropriate records. You could have a job that swaps the SLA of Gold/Bronze SLA Accounts once every minute (contrived, yes, but OH so useful :) Or you could have a series of 5 complex batch apex de-duplication routines that need to be run one after the other, set them all up as separate aggregable jobs, assign orders to them, and have the entire series run once every 15 minutes, every day. Here’s what the SLA swapper example looks like:

How do I use it?
 What do you have to do for your code to fit into the Relax framework? It’s extremely simple. For Individual Jobs, your Apex Class just needs to be Schedulable. For Aggregable Jobs, there are several options, depending on what kind of code you’d like to run. For most devs, this will be Batch Apex, so the most useful option at your disposal is to take any existing Batch Apex class you have and have it extend the “BatchableProcessStep” class that comes with Relax:

// relax's BatchableProcessStep implements Database.Batchable<sObject>,
// so all you have to do is override the start,execute,and finish methods
global class SwapSLAs extends relax.BatchableProcessStep {

	// Swaps the SLA's of our Gold and Bronze accounts

        // Note the override
	global override Database.Querylocator start(Database.BatchableContext btx) {
		return Database.getQueryLocator([
                     select SLA__c from Account where SLA__c in ('Gold','Bronze')
                ]);
	}

	global override void execute(Database.BatchableContext btx, List<SObject> scope) {
		List<Account> accs = (List<Account>) scope;
		for (Account a : accs) {
			if (a.SLA__c == 'Gold') a.SLA__c = 'Bronze';
			else if (a.SLA__c == 'Bronze') a.SLA__c = 'Gold';
		}
		update accs;
	}

        // The ProcessStep interface includes a complete() method
        // which you should call at the end of your finish() method
        // to allow relax to continue chains of Aggregable Jobs
	global override void finish(Database.BatchableContext btx) {
		// Complete this ProcessStep
		complete();
	}

}

That’s it! As long as you call the complete() method at the end of your finish() method, relax will be able to keep infinitely-long chains of Batch Jobs going. Plus, this framework is merely an extension to Database.batchable — meaning you can still call Database.executeBatch() on your aggregable Batch Apex and use it outside of the context of Relax.

Relax in action

In our org, we have 4 jobs set up: 1 individual, 3 aggregable. To kick them off, we select all of them, and change their Active? field to true. Here’s what it looks like after we’ve mass-activated them:

And here’s what the Scheduled Jobs table (accessible through the Setup menu) looks like. Since Case Escalator was set to be run individually, it has its own job. Then there’s a single “Relax Job Scheduler”, which runs every 5 minutes (I’ll probably drop this down to every 1 minute once I officially release the app), checking to see if there are any aggregable Jobs that need to be run, and running them:

Every time Relax Job Scheduler runs, the very first thing it does is schedules itself to run again — regardless of what happens during any processing that it initiates. It queries the “Next Run” field on each Active, Aggregable Job__c record that’s not already currently being run, and if Next Run is less than now, it queues it up to be run as part of a Relax “Process”. Each Process can have an arbitrary number of ProcessSteps, which will be executed sequentially until none remain. If both the current and next ProcessSteps are BatchableProcessSteps, Relax uses a “Process Balloon” to keep the Process “afloat” — essentially launching a temporary scheduled job that is immediately thrown away as soon as the next ProcessStep is begun.

One-off Jobs

Another powerful feature of Relax is the ability to effortlessly launch one-off, one-time scheduled jobs, without having to worry about cluttering the CronTrigger table with another scheduled job. It takes just 1 line of code, AND you can specify the name of the class to run dynamically — e.g. as a String! Reflection ROCKS!!!

// Schedule your job to be run ASAP,
// but maintain the Job__c record so that we can review it later
relax.JobScheduler.CreateOneTimeJob('myNamespace.AccountCleansing');

// Schedule your job to be run 3 minutes from now,
// and delete the Job__c record after it's run
relax.JobScheduler.CreateOneTimeJob(
    'SwapSLAs',
    Datetime.now().addMinutes(3),
    true
);

Try it out for yourself!

Does this sound awesome to you? Give it a test run! Install the app right now into any org you’d like! Version 1.1 (managed-released)

Please send me any feedback you may have! What would make this more usable for you? I’ll probably be releasing the app within the next month or so, so stay tuned!

About these ads

39 Comments

  1. Awesome solution to a very painful problem. The inability in the default UI to “inactive” scheduled jobs for deployments is such a major painpoint that alone is a great feature. To further extend it to enable folks to link batch jobs is icing on the cake. Very cool!

  2. Very promising…though I did try and and got the error “Unable to create an instance of extension ‘JobEditController’ ” when adding a new ‘Job’…i haven’t created any code as yet…just fiddling for now ?

    • Phill, thanks for you feedback! For the app to be useful, you’ll have to create at least one sample schedulable or aggregable Batch Apex class, such as my “SwapSLAs” class, and then create a job tied to it. If you don’t have any such classes created, the app won’t be very useful — I am definitely interested in the “JobEditController” error, though. Did “JobEditController” show up as one of the available “Aggregable Classes” or “Schedulable Classes”?

        • philffdc
        • Posted June 29, 2012 at 11:25 am
        • Permalink

        Zach, I installed in my dev org, went straight to All tabs->Jobs, hit new and then…

        Visualforce Error

        Unable to create an instance of extension ‘JobEditController’

        Figured i’d done something dumb – would love to see this working

      • Phil, I’m going to keep trying to replicate that error — don’t think I’ve ever seen anything like it!

        • philffdc
        • Posted June 29, 2012 at 1:21 pm
        • Permalink

        Got a debug log if you want it…

      • Heck yes, send it my way: zach at skoodat dot com

  3. Do you realistically see jobs starting every five minutes? My experience is that scheduled jobs run between 0 and 10 minutes after the time they’re theoretically ininitiated.

    • Jeremy, that’s a good point. It really depends on the magnitude of the batch process involved. In my experience, small Batch Apex jobs (e.g. ones whose QueryLocators only return < 10000 records) run almost instantly. But large jobs — e.g. ones that involve 1,000,000 records — often linger in the "Preparing" stage for several minutes before kicking off, and then, of course, the actual execution might take a good 10 minutes. But, of course, these are the sorts of jobs that you don't usually run every 5 minutes — more like every 5 days. But for small jobs, execution is almost instant, and there are lots of scenarios (e.g. sending out reminder emails prior to an event) where having the ability to run a process every 5 minutes is critical. And when there's only a few records to process (e.g. just those reminders whose "Send Time" is in the last 5-10 minutes), the batch apex jobs will be small, so they'll get run almost instantly.

  4. Relax lovers! I have finally gotten around to open-sourcing Relax — check out the GitHub repo: https://github.com/zachelrath/Skoodat-Relax

    I won’t be releasing it as a full Managed – Released version until Winter 13, which will bring the ability to execute Batch Jobs sequentially without having to do what Relax currently does: creating ‘temporary’ scheduled Apex Jobs and then destroying them right before starting the next Batch job in line.

    I’ll try to move my example classes (e.g. BatchAccountsUpdater.cls, CaseEscalator.cls, SwapSLAs.cls) into an ‘examples’ directory.

    I’m also (potentially) doing an Unconference Session on Relax at 1 PM tomorrow in DevZone — stop by and we can discuss Batch/Scheduled job hassles and how Relax can help resolve them!

    • Zach,
      Thank you for sharing your efforts on Relax with the community. The timing is perfect for me on my current project; I have been wondering about the release schedule, hoping I might be able to make use of it. This will be a big help to me, and a great benefit to many others out there.

      When I started looking at the code, I got to the 4th line in the trigger and had to stop and come here to post. That ternary as a for loop iterator is SLICK!

      • Thanks Jeff. I am hoping to put up a Managed Released package and put it up on the appexchange soon (so that users don’t have to add this code to their existing code bases, and so that they’ll get all the advantages of the code being part of an AppExchange app (e.g. separate execution context, governor limits, etc.), so please fork and submit a pull request if you have any changes!

  5. Hi Zach,

    Amazing tool!, just what i was looking for.
    Unfortunately installation failed for me.

    “Error Message: The post install script failed.”

    Any idea?

    Thanks

    • The post-install script right now is just spitting out some debug information. I’ve been meaning to put out a new version without it, just haven’t gotten around to it yet. Will let you know when I do. Usually the script succeeds for people, strange that it did not.

      Zach

  6. Zach, I have a question about the whole scheduling with Aggregable Batch Proceses. I set the increment for your two sample batch jobs to run every 10 minutes. But in reality they run every 15 minutes (From looking in the Apex Logs). Can you explain this?

    • Hi Bryan, good question. There are several things that may delay the actual execution time of your processes.

      First, long-running batch processes can take several minutes, sometimes even longer, to finish running, and so each subsequent job in the process chain will not start until its preceding job has finished. This is necessary in order to have all jobs handled by a single Scheduled Apex job. In a future release (feel free to code it yourself and submit a pull request on Github!) I’d like users to have the option of having certain jobs run in parallel — e.g. giving users to have one sequence of jobs executing in a separate, dedicated Scheduled Apex job execution context.

      Second (and more likely the cause of the delay in this scenario) the actual scheduled Apex runtime currently only runs once every 5 minutes to check for jobs that need to be run. This was only done as a precaution while prototyping, and I think it can be safely removed — should be done in a future release.

      Zach

      • Ahh I figured as much thank you. So in the current situation I have right now, I want to schedule 8 Aggregable jobs in order to run once a day at a certain time (Lets say 2:00 A.M.) How exactly would I accomplish this in the scheduling Visualforce page? Would I just create all the aggregable jobs to run once a day and set the next run time to whatever I wanted it to be?

      • Exactly, set all the Aggregable jobs to run once daily, and set the next run time to be 2 AM on the first day (e.g. tomorrow) that you’d like the process to run. If the order of execution matters, set the Order field on each Aggregable job so that lower-numbered jobs will happen before higher-numbered jobs.

      • Perfect, I am going to try setting that line 116 to 1 minute and see what happens. Also when you do release to the AppExchange, I will be giving it 5 stars and an excellent review. This is really really nice.

  7. “Ability to include a parameter” – great idea posted by zjeh regarding passing in parameters/variables to Batch/Scheduled Jobs using Relax. Let me know what you think of my tentative solution! I’ll try to work this in to the first managed release, hopefully coming in the next month.

  8. Zach, I installed Skoodat Relax in my DE. Now, all I need to do is schedule my individual job to run every minute. I have created the Apex class that implements schedulable with execute method that does the required operations. Now, how do I schedule this to run every minute? I go to Jobs tab and try to schedule it, but I see no apex classes in the “Aggregable Apex Class” picklist. None of the relax example classes show up here as well. Now, if I go to the default Salesforce Scheduler, it shows my class as well as the relax example classes. Is there any step I’m missing?

    • Madhu, in order for your class to run every minute, it will need to be an Aggregable Class (it can be a Schedulable class as well, but classes that are just Schedulable cannot be run every minute). To make it an Aggregable class, the easiest thing to do would be to have your Apex class extend the “relax.BatchableProcessStep”, as in the “SwapSLAs” example (this class is available on the GitHub page). This will reformatting your code into Batch Apex form, but you can essentially just have a dummy start method that does a query that will only return one row, e.g. a query on the User account with limit 1. THen in the execute method you can run your logic. That’s the easiest way to do it right now.

      However your question suggests the need for a “SchedulableProcessStep” class. I have added this as an enhancement: Add SchedulableProcessStep to allow easy incorporation of Schedulable jobs into Relax processes

  9. Zach, Thanks for your response. I did what you suggested and ceated my class ProcessResultsQueueBatch that implements relax.BatchableProcessStep. When I try to create the job, the class shows up now in the picklist. But when I try to save, it errors out with Error: ‘ProcessResultsQueueBatch’ is not a valid Apex Class. It is a valid apex class, so not sure why it says that. Is there any that it is looking for? Also when I select “run individually” my other class that implements schedulable shows up fine. Can I set the cron schedule to run every minute? Even when I use run individually option, I see the same error when try to save. However, if I use your example classes, it saves fine.

  10. Hi,

    Even in this case we need to reschedule atleast the (one) job that you are creating during production deployment right?

    Regards,
    Sathya

  11. I mean during production deployment, I should unschedule and reschedule atleast the (one) job that you are creating right?

    • Sathya,

      No, you don’t have to manually schedule/unschedule it — all you have to do is mark your Job__c records as Active/Inactive, and Relax will automatically schedule/unschedule its one “Job Scheduler” job, and any Individually Scheduled jobs, as needed. Salesforce shouldn’t make you unschedule Relax’s Scheduled Job when you’re doing unrelated deployments — unless the code that you are pushing references Relax’s Job Scheduler code or the code for any Individually Scheduled jobs.

      Zach

  12. You say:

    A third issue often cited by developers is the frequency at which jobs can be run. Through the UI, a single CronTrigger can only be scheduled to run once a day. Through code, you can get down to once an hour. If you wanted to, say, run a process once every 15 minutes, you’d have to schedule the same class 4 times — using up 4/25 (16%) of your allotted scheduled jobs — and you have to manage this through Apex, not the UI.

    But why can’t you set a cron expression for the job as 0 0/15 0 * * ?

    Wouldn’t that run every 15 minutes and only be one job?

    • No, that is not possible from within Apex — try it, and you’ll see that Apex only supports integers 0-59 for the second and minute parts of the CRON expression. So you’d get an error if you tried to use a schedule an Apex job by using a CRON expression that contained any special characters, such as “/”, within the second or minute “0 0/15 …. ” portions. See the docs: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_scheduler.htm

        • radiusx2
        • Posted May 9, 2013 at 3:16 pm
        • Permalink

        You are correct.

  13. Hi Zach,
    Thanks for an excellent insight about this app.I have successfully installed in my developer edition but i am getting a TImeLimit exceeded issue when i am clicking on the New Job :-( . I am not sure if everybody is facing the same issue or its particular to me. Any help in this regards will be greatly appreciated.

    Sai

    • Hello Sai,

      Thanks for your feedback! I am guessing that the reason you’re getting “TimeLimit exceeded” is that you have a lot of ApexClasses in your org. The current version of Relax loops over all ApexClasses in your org and tries to determine if they implement relax.BatchableProcessStep, if you are doing an AggregableJob, or Schedulable, if you are doing an Individual Job. I am considering making the ApexClass picker an AutoComplete instead of a dropdown, which would eliminate this problem.

      Regards,

      Zach

        • SaiRakesh
        • Posted June 20, 2013 at 1:04 pm
        • Permalink

        Hi Zach,
        Thanks for the quick reply. Do we have a limit on the number of classes the code can scan?If so what was the limit as i have around 350 classes in my production org. I have tested this in my developer edition where the number of classes were lessthan 20. Do you anticipate this issue to be fixed in next release Zach, if so do you have any time limit.Thanks in Advance zach.

      • Sai,

        I have just released Beta 5, which has the changes I described. Since this is a Beta package still, you’ll have to Uninstall and install the new version. This version incorporates an Autocomplete search on Apex Classes instead of a Picklist, so this avoid any issues related to the number of classes in an org. Let me know if this resolves the issue.

        Regards,

        Zach

        • SaiRakesh
        • Posted June 20, 2013 at 3:56 pm
        • Permalink

        Thanks Zach.I think the old issue is fixed and i am able to create a new Job record. I will be deploying in my sandbox environment and test the same.Thanks once again for the timely help and prompt response.

  14. Announcing a Managed Released version of Relax, complete with a powerful new feature: Job Parameters. When defining the time and run increment of your Jobs, you can now also pass in Parameters, as a String (hint hint JSON!), which are available in your Relax jobs. Relax also comes pre-loaded with two Relax-usable Apex Classes that leverage this new feature: MassUpdate, and MassDelete. Both let you pass in an arbitrary SOQL query in your Job Parameters, and then perform a Batch Update / Delete operation on those records.

    To install the Managed Released version (1.1), and more details on how to use these new classes, head over to the updated Relax Github repo: https://github.com/zachelrath/Relax

    Relax should be available as a free app on the AppExchange before Dreamforce 2013.

    Also, I’ll be doing a 30-minute session on Relax during Dreamforce 2013! Once Agenda Builder goes live, be sure to register for the session.

  15. Hi, Zack.
    Do you have separate cut-off version that solves only problem #4?
    I was going to write my own solution of this, but it seems like I’m inventing a bicycle if you have already done this.
    However, I am not really currently interested in problems 1, 2, 3 you mentioned.

    • No I don’t have a separate version — but you can easily still use Relax for 3/4 without ever using the functionality that does 1/2. When creating a new relax__Job__c record, just choose the “Individually Scheduled” option (I think that’s what it’s called), which will ask you to type the name of a Schedulable Apex class and provide a CRON Schedule as a String. You can ignore the other type of Job that Relax lets you create which is used for 1/2.

      https://github.com/zachelrath/Relax

      • Thanks, Zack, for the quick response.
        I have tried your package and it seems it doesn’t transform existing scheduled jobs into its custom objects, so it doesn’t actually solve my problem, where I want just to collect information about existing scheduled jobs, scheduled by user or programmatically, but not from your custom page or my custom page, mass cancel them before deployment and mass reschedule them after deployment. So I written my own custom page which does only what I actually need. It is not actually so sophisticated as your page, but it satisfies me.
        Also I have written more thoughts about it in my blog here:
        http://patlatus.wordpress.com/2014/01/03/automating-the-process-of-cancelling-and-rescheduling-scheduled-jobs-in-salesforce/


One Trackback/Pingback

  1. [...] for those of you who were excited about Skoodat Relax, you may have just realized how it all works, and you are absolutely right! One of the features of [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: