We need to implement the following pattern at my org:
- callout to external data source
- if that callout takes too long (according to some configurable threshold), log an error (ie do some DML)
- if that callout timed out on the remote server, try it again
Recognizing the potential for the dreaded "You have uncommitted work pending. Please commit or rollback before calling out." error, I put the error logging code in a future method, thus isolating the DML from the callouts. However, the error is still being thrown. I reduced the issue down to this pattern:
public static void foo() {
Http http = new Http();
HttpRequest req = new Httprequest();
req.setEndpoint('https://test.salesforce.com'); //whatever endpoint
req.setMethod('GET');
http.send(req); //works fine
bar();
http.send(req); //throws calloutexception
}
@future public static void bar() {
}
Am I correct to assume that calling a future method counts as a DML operation? Is there any documentation I'm missing somewhere?
Here is the log for the above code snippet:
Error on line 8, column 1: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
AnonymousBlock: line 8, column 1
41.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WAVE,INFO;WORKFLOW,INFO
12:41:06.606 (606820392)|UNKNOWN|[EXTERNAL]
12:41:06.606 (606893134)|EXECUTION_STARTED
12:41:06.606 (606904404)|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
12:41:06.606 (608097326)|SYSTEM_MODE_ENTER|false
12:41:06.606 (608671843)|CALLOUT_REQUEST|[6]|System.HttpRequest[Endpoint=https://test.salesforce.com, Method=GET]
12:41:06.606 (737558005)|CALLOUT_RESPONSE|[6]|System.HttpResponse[Status=OK, StatusCode=200]
12:41:06.606 (862244488)|EXCEPTION_THROWN|[8]|System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
12:41:06.606 (862467817)|SYSTEM_MODE_EXIT|false
12:41:06.606 (862668955)|FATAL_ERROR|System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
AnonymousBlock: line 8, column 1
12:41:06.862 (862819672)|CUMULATIVE_LIMIT_USAGE
12:41:06.862 (862819672)|LIMIT_USAGE_FOR_NS|(default)
Number of SOQL queries: 0 out of 100
Number of query rows: 0 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 0 out of 150
Number of DML rows: 0 out of 10000
Maximum CPU time: 0 out of 10000
Maximum heap size: 0 out of 6000000
Number of callouts: 1 out of 100
Number of Email Invocations: 0 out of 10
Number of future calls: 1 out of 50
Number of queueable jobs added to the queue: 0 out of 50
Number of Mobile Apex push calls: 0 out of 10
12:41:06.862 (862819672)|CUMULATIVE_LIMIT_USAGE_END
12:41:06.606 (862919688)|CODE_UNIT_FINISHED|execute_anonymous_apex
12:41:06.606 (864947070)|EXECUTION_FINISHED
I have also posted this question on the developer forums.