Solution to : “You have uncommitted work pending” in Salesforce

Deep Banerjee
3 min readJan 19, 2024

--

In the Spring ’24 Salesforce release, an important enhancement was introduced to the way callouts are handled after rolling back transactions. The new Database.releaseSavepoint() method allows developers to explicitly release savepoints, addressing limitations with callouts after rolling back transactions.

This blog post will cover the details of this enhancement, explain the issues it aims to solve, provide examples of how to make use of Database.releaseSavepoint() correctly, and discuss some best practices to keep in mind when working with savepoints and callouts in Apex.

The Problem

Previously, attempting to make a callout after creating a savepoint in a transaction would result in an error, even if the transaction was rolled back. This prevented developers from being able to integrate callouts with complex transactions, which was a significant limitation

The Solution: Database.releaseSavepoint()

The new Database.releaseSavepoint() method allows developers to explicitly release savepoints, enabling callouts to be made subsequently within the same transaction. This unblocks several important use cases and improves the ability to integrate external services.

Examples and Use Cases

Here are some examples of how to use Database.releaseSavepoint() in different scenarios:

  1. Wrapping a Callout in a Transaction: A common use case is wanting to wrap an external API call in an Apex transaction to encapsulate it. In this case, you can create a savepoint before the callout, roll back the transaction if needed, and then release the savepoint to enable subsequent callouts within the same transaction
Savepoint sp = Database.setSavepoint();
try {
// Call external service...
} catch (Exception e) {
Database.rollback(sp);
Database.releaseSavepoint(sp);
}

2. Rolling Back Transactions: If you need to roll back a transaction, you can create a savepoint before the DML operations, roll back the transaction if needed, and then release the savepoint to enable subsequent callouts within the same transaction

Savepoint sp = Database.setSavepoint();
try {
// Perform DML operations...
if (someCondition) {
Database.rollback(sp);
}
// Make callouts...
} catch (Exception e) {
Database.rollback(sp);
Database.releaseSavepoint(sp);
}

Best Practices

When working with savepoints and callouts in Apex, it’s essential to follow these best practices:

  • Always release savepoints explicitly after making callouts to ensure proper transaction handling
  • Use savepoints only when necessary, as they can add complexity to your code and potentially lead to issues if not used correctly.
  • Test your code thoroughly to ensure that savepoints and callouts are working as expected.

In conclusion, the Database.releaseSavepoint() method introduced in the Spring '24 Salesforce release is a valuable enhancement that addresses limitations with callouts after rolling back transactions. By following best practices and understanding how to use this method correctly, developers can improve their ability to integrate external services and handle complex transactions in Apex.

NOTE:

For Apex tests with API version 60.0 or later, all savepoints are released when Test.startTest() and Test.stopTest() are called. If any savepoints are reset, a SAVEPOINT_RESET event is logged.

Before API version 60.0, making a callout after creating savepoints throws a CalloutException regardless of whether there was uncommitted DML or the changes were rolled back to a savepoint. Also, before API version 60.0, each rollback call incremented the DML row usage limit.

Thanks for reading!

Read more here : Transaction Control

--

--