Giter Site home page Giter Site logo

relax's Introduction

Relax

Taking all the pain out of Force.com Batch and Scheduled Job management.

What Relax lets you do:

  1. Run multiple batch/scheduled Apex jobs as often as every 1 MINUTE, every day
  2. Mass activate, unschedule, and change ALL of your Scheduled and Batch Apex jobs at once --- minimizing the hassle of code deployments involving Scheduled Apex
  3. Mix-and-match your Batch Apex routines into "chains" of jobs that run sequentially on a scheduled basis, without hard-coding the sequences into your code
  4. Define string/JSON parameters to pass into Relax jobs you create, allowing for massive reuse of your Batch/Scheduled Apex.
  5. Bonus: powerful 'MassUpdate' and 'MassDelete' Apex Classes that can be run as Relax Jobs are pre-included! Never write another line of Scheduled Apex just to do mass-update a million records!

For an intro to Relax, check out this blog post:

Relax: Your Batch Scheduling Woes are Over

Installation

Latest versions:

v1.4 - released 9/14/2013

Using Relax

Relax is available as a managed released package (coming to AppExchange before Dreamforce 2013!), click here to install v1.4. now!

There are several sample Batch / Scheduled Apex classes that work with the Relax framework included in the src/classes directory. MassUpdate.cls and MassDelete.cls are included in the Relax managed package, while BatchAccountsUpdater.cls and CaseEscalator.cls are just some other examples:

  1. MassUpdate.cls
  2. MassDelete.cls
  3. BatchAccountsUpdater.cls (finds all Accounts with a null Industry and gives them an Industry)
  4. CaseEscalator.cls (finds all New Cases created more than 2 days ago whose Accounts have a Platinum/Gold SLA that are NOT High/Critical priority, and escalates them

Here's what MassDelete looks like --- it's very simple:

global class MassDelete extends relax.BatchableSchedulableProcessStep implements Database.Stateful {

	global String parameters;

	public String query;

	global override Database.QueryLocator start(Database.BatchableContext btx){

		// Attempt to retrieve parameters from our Job record
		// if we do not have parameters yet.
		if (parameters == null) parameters = params();
		if (parameters != null) {
			// We expect our parameters to be a JSON object,
			// so deserialize it
			Map<String,Object> paramsObj;
			try {
				paramsObj = (Map<String,Object>) JSON.deserializeUntyped(parameters);
				query = (String) paramsObj.get('query');
			} catch (Exception ex) {
				// Complete our batch process
				complete();
				throw ex;
			}	

		}

		if (query != null) {
			return Database.getQueryLocator(query);
		} else {
			// Return a dummy query locator
			return Database.getQueryLocator([select Id from User where Id = :UserInfo.getUserId() limit 0]);

		}	
	}

	global override void execute(Database.BatchableContext btx, List<SObject> scope) {
		if (scope != null && !scope.isEmpty()){
			Database.delete(scope,false);
		}	
	}

	global override void finish(Database.BatchableContext btx) {
		// Continue our Batch Process, if we need to
		complete();
	}

	// Implements Schedulable interface
	global override void execute(SchedulableContext ctx) {
		Database.executeBatch(new MassDelete());
	}

}

Here's what CaseEscalator looks like:

public class CaseEscalator extends BatchableProcessStep implements Schedulable {

	public override Database.Querylocator start(Database.BatchableContext btx) {
		// Find all Cases that have been open		
		return Database.getQueryLocator([
			select	Priority
			from	Case 
			where	Status = 'New'
			and		Priority not in ('High','Critic
			
			al')
			and		Account.relax__SLA__c in ('Platinum','Gold') 
			and		AccountId != null
			and		CreatedDate < :Date.today().addDays(-2) 
		]);
	}

	public override void execute(Database.BatchableContext btx, List<SObject> scope) {
		List<Case> cases = (List<Case>) scope;
		for (Case c : cases) {
			// Set the Priority to 'High'
			c.Priority = 'High';
		}
		update cases;
	}

	public override void finish(Database.BatchableContext btx) {

		// Continue our Batch Process, if we need to
		complete();
	}

	// Implements Schedulable interface
	public void execute(SchedulableContext ctx) {
		CaseEscalator b = new CaseEscalator();
		Database.executeBatch(b);
	}

}

Unit Tests

All Relax Unit Tests are currently stored in UnitTests.cls and JobEditController.cls, and should pass in any org. To run these tests, navigate to "Apex Test Execution" in your org and select the "relax" namespace, then select both of the above classes and Run Tests.

Using the pre-included MassUpdate class in a Relax Job:

The MassUpdate class makes use of Relax Job Parameters. Basically, when creating a new Aggregable Relax Job, you'll see a text box called "Parameters". MassUpdate expects you to provide some JSON here defining what kind of Mass Updates you want to do. For instance, putting the following JSON into the Parameters box will find all Account records whose Name field starts with 'Extravagant', and set their relax__SLA__c field to 'Platinum':

{
  "mode":"FIELD_WITH_VALUE",
  "query":"select relax__SLA__c from Account where Name like 'Extravagant%'",
  "field":"relax__SLA__c",
  "value":"Platinum"
}

Here's what it looks like to create this Job from within Relax:

Sample Relax Job: Change SLA of Extravagant Accounts

(Activate the Job and change its Run Increment as desired, of course).

Here are all supported Relax operation modes:

  • FIELD_WITH_VALUE mode: updates a particular field with a particular value. Example given above.

  • FIELDS_WITH_VALUES mode: updates a set of fields with corresponding values. Example: finds all Cases that have been Open for over 30 days, and sets their Priority to Critical and Escalates them:

     	{
     	  "mode":"FIELDS_WITH_VALUES",
     	  "query":"select Priority, IsEscalated from Case where Status = 'Open' and CreatedDate < LAST_N_DAYS:30",
     	  "valuesByField":{
     		 "Priority":"Critical",
     		 "IsEscalated":true
     	   }
     	}
    
  • FIELD_FROM_FIELD mode: for each row, copies the value of a source field into a target field. Example: copies the value of the OwnerId field of each Opportunity into a custom Owner2__c field:

     	{
     	  "mode":"FIELD_FROM_FIELD",
     	  "query":"select OwnerId, Owner2__c from Opportunity where Owner2__c = null",
     	  "sourceField":"OwnerId",
     	  "targetField":"Owner2__c"
     	}    
    
  • FIELDS_FROM_FIELDS mode: same as Field from Field, but for multiple source-to-target field pairings: Example:

     	{
     	  "mode":"FIELDS_FROM_FIELDS",
     	  "query":"select OwnerId, Owner2__c, CloseMonthFormula__c, CloseMonth__c from Opportunity",
     	  "sourceFieldsByTargetField":{
     		 "CloseMonth__c":"CloseMonthFormula__c",
     		 "Owner2__c":"OwnerId"
     	  }
     	}    
    

Support / Contributing

Relax is open-source, and we would LOVE it if you would like to contribute some example classes that work with Relax, add features to the code, whatever! Just follow the steps below to contribute:

  1. Fork Relax.
  2. Create a branch (git checkout -b my_markup)
  3. Commit your changes (git commit -am "Modified batch scheduling engine.")
  4. Push to the branch (git push origin my_markup)
  5. Open a [Pull Request][1]

Licensing

Relax is distributed under the GNU General Public License v3 -- see LICENSE.md for details. If you want to use Relax or a modified version of it in your own organization, that is okay, but code from Relax may not be redistributed or sold for profit in another product.

relax's People

Contributors

zachelrath avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

relax's Issues

Scheduled job not running in Sandbox

Using the relax.MassUpdate class:

{
"mode":"FIELD_WITH_VALUE",
"query":"select Gainsight_Edit_c from Implementation__c where Gainsight_Edit_c = TRUE",
"field":"Gainsight_Edit_c",
"value":FALSE
}

The Next Run time passes regardless of how I setup the job frequency. When I try to navigate to the Job Scheduler Id it says the URL no longer exists.

Error appears on job creation

Error:
relax.Job: execution of BeforeInsert caused by: System.NullPointerException: Attempt to de-reference a null object External entry point (relax)
appears on job creation.

Probably some fields are not marked as required.

relaxjobcreationbug

Relax runs jobs multiple times before completing

I have a few classes that extend relax.BatchableProcessStep and are created through JobScheduler.CreateOneTimeJob() with a start time of Datetime.now().addMinutes(-5).

After a few minutes, the job kicks off, and I can see through the dev console that it's running.

However, I find that one minute after the batch has started, the same batch class has been spawned again with the exact same parameters. Sometimes it's 1 extra copy, other times it's 2 extras.

There's no error taking place in the batch classes (I've surrounded them with try/catch), and the complete() method is being used at all exit points.

Attaching two files:

  • Listing of Apex Jobs, see "UpdateItemStatusBatch": screen shot 2016-12-16 at 16 07 38
  • Output from the jobs that has been sent into Slack. Exactly the same output from the batch running twice. screen shot 2016-12-16 at 22 11 15

I looked through the code, and wonder if this is what's happening:

  1. Job kicks off and spawns the batch
  2. In the meantime, the job scheduler runs again and sees that the job is still "Queued" (I've never seen it have a status other than "Queued" or "Completed"), that the next run time is before now and runs it again.
  3. By the time the job scheduler runs again, both instances have been completed.

So...

  1. Is there a reason why the job status never gets updated?
  2. Alternatively, if #1 is working as expected, can JobScheduler.GetNextRunTimeForJob() check if it's a one-time job and has already been created? In which case, it doesn't return a run time.

FWIW, I tried setting up the code on my own sandbox, but ran into a ton of errors while deploying it. Else I would have tried to fix it myself.

Quick user Tutorial

I checked out the Readme, but it doesn't really have a "User Guide". I am getting an Error that

"
Error:
'Xactly.XactlySMBImportScheduler1' is not a valid Apex Class.
"

and I don't know how to make this scheduled Apex class into a valid Apex class for your app...

any help would be greatly appreciated!

thanks

relax.UnitTests.TestSimpleProcessAdvancement -- System.AssertException: Assertion Failed: Expected: 30, Actual: 25

The error:
relax.UnitTests.TestSimpleProcessAdvancement -- System.AssertException: Assertion Failed: Expected: 30, Actual: 25
seems to happen spuriously in our non-Prod environments (probably once every 15 runs-or-so).

It hasn't happened during a Prod release, yet... but when it does we will be sad because it'll invalidate a test run of over 5000 tests that takes ~2.5 hours to run.

I don't see how this test is failing... but it appears to be.

This is for the Managed Package v1.4

Add SchedulableProcessStep to allow easy incorporation of Schedulable classes into Relax processes

Currently Schedulable classes have to be coerced into the Batch Apex model in order for them to be considered 'Aggregable', and hence included in Relax Aggregate Processes by extending the BatchableProcessStep class.

What would be helpful is to have a SchedulableProcessStep class that Schedulable Apex classes can easily extend to make them candidates for Relax Aggregable processes. Along with this would need to exist a SchedulableRunnable class to kick off the Scheduled Job, but this should be doable.

Unit Tests failing in beta 5

BatchAccountsUpdater class is used in unit tests, but is not included in beta 5 managed package.
"System.DmlException: Insert failed. First exception on row 0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, 'relax.BatchAccountsUpdater' is not a valid Apex Class."

image

Could not perpetuate Job Scheduler Process

I received an error which stated:
Message: Based on configured schedule, the given trigger will never fire.
Line Number: 121
Stack Trace:(relax)

Since all of my schedules are set to "Run Individually" they're all still processing but the Last Run Date is not being updated on the View in Salesforce. Any way I can manually restart the JobScheduler?

Getting exception when trying to schedule a job

Hi Zach,

I just want to see if you can help with the below error message.

One of our developer has installed the Relax package and I am trying to schedule the job with an active user and I am receiving the below error message.

Method is not visible: Void relax.jobscheduler.()

Please let me know if you need any info from my end.

Thansk,
Shanmuka

Ability to Include a Parameter (enhancement)

Hi Zach,

A nice enhancement would be having ability to include a parameter with the scheduling. Have you given this any thought?

I am thinking a single string that will be passed in the Apex call. I have seen some batch classes where all or part of the query to use is passed in.

Change ApexClass selection options from Picklists to Autocompletes

The Picklists allowing you to select either a Schedulable ApexClass or one that implements Relax's ProcessStep interface should be Autocomplete "Lookups" on the ApexClass object, instead of Picklists. This will accomodate orgs with 100's of ApexClasses, in which case this makes the page take a long time to load as it builds these Picklists, and will make it easier to get at the ApexClass you want faster.

Add a reporting mechanism

One thing I really miss in Apex Jobs, is a way to see what happened -

  • What records were processed?
  • What records succeeded?
  • and most importantly - What records failed? and why?

a simple solution would be to add a summary mail, like this:
#SaveResultMailer

another, perhaps better approach, is to add a wrapped custom object, that keeps info about processing a status

I might implement one of these

Jobs__c.Parameters__c missing from master HEAD

  1. classes/BatchAccountsUpdater.cls -- Error: Dependent class is invalid and needs recompilation:
    Class.BatchableProcessStep: line 1, column 52
    SimpleProcessStep: line 15, column 12: Invalid field Parameters__c for SObject Job__c (line 1, column 43)

...etc. blah blah for every reference

Of course it's there in the package :-) Thanks! --Eric ([email protected])

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.