You need to sign in to do that
Don't have an account?

Multiple web service calls in a transaction
Hi,
I have an Apex program that loops through database records, makes a web service call for each, and stores the result in the record. The web services uses a token which expires after 24 hours, so a record may make up to 3 web service calls -- 1 to fail, 1 to get the token, and 1 to succeed.
From my understanding SalesForce limits web service calls to 10 per database transaction so as a brute-force method, I am doing an update for each record like this:
// initialize wrapper, scope
AccountWrapper wrapper = new AccountWrapper();
List<Account> scope = [SELECT Id, Name, BillingStreet, BillingCity, BillingState, BillingPostalCode FROM Account LIMIT 10];
// get districts
for (Account account: scope) {
wrapper.getDistrict(account, this.api);
update scope; // update scope to stay at or under Transaction Web Services governor limit
}
The Http web service requests are in wrapper.getDistrict. When I run this, I get a CalloutException:
recSystem.CalloutException You have uncommitted work pending. Please commit or rollback before calling out
My guess is that the update scope is not committing the transaction. How do I do a commit, or is there a better approach?
My google geocoding engine for salesforce (link here) is a good working example of how to call out while having DML.
All Answers
You can't force the commit. The commit will automatically happen at the end of the apex code execution.
The issue is that you are using a DML command (in this cae update) in between service calls.
You need to issue your DML command (in this case update) out side of your loop. Program this like a trigger - NO DML calls in side of loops!
Also, it is 10 http callouts per apex code execution. So if you need a max of 3 call outs per record, then you could only process 3 records which would give you a max of 9 call outs per execution and stay below the 10 callouts.
If you don't move your update outside of the loop, then you will not be allowed to call out again and you will get the uncommitted work error. You simply can't make a call out after a DML statement.
I think that the commit will happen once the apex code has stopped executing (i.e. a transaction) all together. It has nothing to do with a scope (of a function or variable). This is why you can't explicitly commit to the database.
My google geocoding engine for salesforce (link here) is a good working example of how to call out while having DML.
Thanks, I'm reading your article now. Nice blog, good content and style!
Thanks for the comments. Hopefully you can take that example, remove the geocoding part, and get it working. Then you can use some kind of filtering method (like I did with the date check) and schedule the batches to get the call outs going.
Excellent example, and very much like what I need.
I think the difference is that I can't cache my result -- the client runs my program when congressional districts change, a one-time event which essentially invalidates anything in my 'cache'. But what I could do is run my updater to mark all my districts as N/A or in need of resetting, and let the scheduled job find them.
Does that sound right to you?
If you are storing this in the database, then you are caching it out :-) You just aren't updating it every time something changes (like my geo-coder is.). So in your case the last geocode date being compared to the last modified date won't work for you.
That being said, you could use NA in your SOQL to filter and prevent the batch from running on the same set every time. That is assuming you re-set all of your records to NA before scheduling your class to run and your schedule future method (i.e. call out) sets the fields to something other than NA.
It sounds like you are on the right track to me.