Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I am using an APEX batch to process and insert large amounts of data. Essentially I would like it to process every row to catch any errors with the data and then rollback the entire batch if a single error is found. Is there any simple way to do this?

I have tried using a Save Point, but because they are not serialisable, it doesn't work across the entire batch.

Just to clarify, by error I mean an error that I have defined, error with validation, etc. Not an Exception thrown by the system.

share|improve this question
add comment

3 Answers 3

up vote 2 down vote accepted

Batch Apex and Transaction Scope. Unfortunately the platform does not wrap a single Database transaction around the whole job. This is not possible due to the amount of data and length of the jobs (likely Oracles commit log would break internally anyway). So it breaks the job into chunks/scopes determined by a parameter on the Database.executeBatch method (defaults to 200 records). Each of these scopes is passed to your 'execute' method, this represents a single transaction and here you can rollback changes if needed. For more information see the documentation here.

Each execution of a batch Apex job is considered a discrete transaction. For example, a batch Apex job that contains 1,000 records and is executed without the optional scope parameter from Database.executeBatch is considered five transactions of 200 records each. The Apex governor limits are reset for each transaction. If the first transaction succeeds but the second fails, the database updates made in the first transaction are not rolled back.

Rollback/Error Recovery. Given the above you have a few options to consider, though the most scalable is to ensure that your job can be re-run incrementally (only for records still not processed via a flag of some kind on the records being processed) once the errors have been addressed. If your data volumes are low enough you might be able to implement your rollback logic in your 'finish' method. Here is a blog post I wrote that talks about the options and design considerations (which are more of a functional user experience consideration in reality).

share|improve this answer
    
Doing the rollback in the finish method is something I've considered, but as you said, for large amounts of data this could run into problems. Is there any huge downsides to maybe launching a database insert/update batch from an error checking batch? –  danpker Feb 6 '13 at 11:50
    
Not really, but I would personally still look to see if I can engineer the main batch process to be able to be run over and over using some 'processed' flag on the source records. This way you can reuse the same logic more easily. However this choice really depends on knowing more about the use case. I think you've got the general idea. Hope this helps! –  Andrew Fawcett Feb 6 '13 at 13:41
add comment

I don't think there is an easy way to do this, as you've said you cannot use Savepoints across iterations of the execute method. I do not believe there is a system supplied method of doing this and you might have to consider rolling your own.

I guess it depends on the complexity of what you are doing and how difficult it would be to rollback manually (i.e. either delete records or update changes), certainly some trigger logic or validation rule may prevent you from doing this.

It might be safer/more appropriate to pre-validate before writing to the DB in the first place. Good luck, and hopefully someone has a better answer.

share|improve this answer
add comment

One possible solution I can think of is a staging object as an intermediate step and chained batch executions.

Let's say you're doing something with Accounts. First create a custom object called Account_Batch_Staging__c. Make it an exact duplicate of the account object, including as much validation of the original object as possible (with some standard objects this is difficult, so you might need an Apex trigger to accomplish this completely). Include an additional fields: either a lookup to store the real Account Id, or perhaps an External Id field to do the same.

Stage 1: Execute batch logic while moving data from Account to Account Batch Staging
Stage 2: Evaluate for your defined error condition
Stage 3: If not errors, enqueue a second batch in the finish of the first moving data from Account Staging back to Account.

If there are errors, you could either abort, clean up the staging table with a different batch, or perhaps retry.

It isn't fool proof, but it does give you a safe landing at the half-way point that is much more under your control to roll-back. Oh...and if you're short on custom objects, this does, of course, burn a custom object.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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