2

Lightning tries to 'boxcar'/bundle/batch individual Server-Side calls, to save bandwidth (see https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/controllers_server_actions_queue.htm).

However, when it is batching two calls where the first is doing DML, and the next is doing callouts, then in the second call I get the well-known "You have uncommitted work pending. Please commit or rollback before calling out". Apparently because the batched calls run in one thread, and the DMLs from the first call actually prevent the second call from doing callouts.

Are there any Salesforce engineers here that can confirm that?

For now, the only ugly workaround I can think of is to ensure that in the Lightning Component, I work using setTimeout and $A.getCallback() to delay the second call so it is not batched with the first call.

Are there other solutions I could try?

3

If the calls are unrelated, call setBackground(true) on one of the two actions. This will split the actions in to two processes. The background process will occur "later":

var f1 = cmp.get("c.action1"), f2 = cmp.get("c.action2");
f1.setBackground(true);
f1.setParams({...});
f2.setParams({...});
$A.enqueueAction(f1);
$A.enqueueAction(f2);

If the second call depends on the first, then you can use a Promise chain:

new Promise((resolve,reject) => {
  ...
}).then(value => {
  ...
});

If you call the callout method first, then the DML method second, you won't be violating the DML before callout rule, and you can then skip using setBackground or a Promise chain, and still get maximum performance for your initialization handler.

| improve this answer | |
  • Thanks. Yes I did see that in the docs, but the background boolean is 'officially' meant for "low priority, long-running action" and it is stated that "Don’t rely on each background action being sent in its own request as that behavior isn’t guaranteed and it can lead to performance issues. Remember that the motivation for background actions is to isolate long-running requests into a separate request to avoid slowing the response for foreground actions". So I'm a bit unsure about whether it is advised to use setBackground() to separate my processes? Have you used it before? – Willem Mulder Feb 10 '18 at 21:30
  • @WillemMulder Background processes may be queued together, but if you use one foreground and one background, then they'll be separate transactions. But, like I also said, you can use a Promise chain, but it'll be about the same speed either way. I wouldn't sweat it too much. – sfdcfox Feb 11 '18 at 4:53
  • It still doesn't feel like the right solution to the problem, but I guess it's the best solution for now, given Salesforce's buggy behaviour. So for now I'll go with using setTimeout() or setBackground() but I certainly expect Salesforce to fix it on their end and change the boxcarring functionality to not share governor limits between boxcarred requests. Thanks! – Willem Mulder Feb 12 '18 at 9:53
  • @WillemMulder I had a talk with someone at SFDC previously, and apparently the shared governor limits were intentional. I don't recall who said it or how official the answer was, but apparently, it's by design. Say, maybe you could also reverse the calls? By doing the callout first, the DML wouldn't block it? This might give you slightly better performance. – sfdcfox Feb 12 '18 at 16:01
  • Thanks for that! I hope that Salesforce will at some point update their docs with an official statement about the shared limits, along with a rationale and possibly alternatives. Thanks for the suggestion, you might add that to your answer? – Willem Mulder Feb 13 '18 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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