Transaction Finalizers are a powerful feature in Salesforce Apex that allow developers to define clean-up or follow-up actions which should be executed after a transaction completes, whether it succeeds or fails. This feature is particularly useful in handling post-transaction logic in a structured and reliable way, ensuring that certain operations are performed after the primary business logic has been executed.
What are Transaction Finalizers?
Transaction Finalizers in Apex are classes that implement the System.Finalizer
interface. This interface enables the class to define a method called execute
that acts as a callback method. This method is automatically invoked after the transaction ends, providing a way to perform operations such as logging, sending notifications, or handling rollback operations in case of transaction failure.
Use Cases
Some common use cases for Transaction Finalizers include:
- Error Logging: Logging errors after a transaction fails to execute, which can help in debugging and maintaining the application.
- Notification: Sending notifications (emails, push notifications) after a transaction completes or fails.
- Data Cleanup: Handling any necessary cleanup operations post-transaction.
- Resource Release: Releasing resources that were locked or reserved during the transaction.
Example of Transaction Finalizers in Apex
Below is an example that demonstrates how to use a Transaction Finalizer in a Salesforce Apex class. In this example, we’ll assume a scenario where a record needs to be updated, and regardless of the transaction’s success or failure, a clean-up action (like sending a notification email) is required.
public class UpdateAccountHandler { public class AccountUpdaterFinalizer implements System.Finalizer { private String accountId; private Boolean isSuccess; public AccountUpdaterFinalizer(String accountId) { this.accountId = accountId; this.isSuccess = false; } public void execute(System.FinalizerContext context) { // Check if the transaction was successful isSuccess = context.isSuccess(); // Logic based on the transaction outcome if (isSuccess) { System.debug('Transaction succeeded for Account ID: ' + accountId); // Further success actions, e.g., sending success email } else { System.debug('Transaction failed for Account ID: ' + accountId); // Error handling actions, e.g., sending error notifications } } } public static void updateAccount(String accountId) { // Registering finalizer at the beginning of the transaction AccountUpdaterFinalizer finalizer = new AccountUpdaterFinalizer(accountId); System.attachFinalizer(finalizer); try { // Main logic to update account Account acc = [SELECT Id, Name FROM Account WHERE Id = :accountId LIMIT 1]; acc.Name = 'Updated Name'; update acc; // Indicate success in finalizer finalizer.isSuccess = true; } catch (Exception e) { // Error is handled by finalizer finalizer.isSuccess = false; throw e; } } }
Key Points
- Registration: A finalizer is registered to a transaction using
System.attachFinalizer
. - Execution Context: The
execute
method of the finalizer receives aFinalizerContext
object, which can be used to check whether the transaction succeeded or not. - Limitations: Remember that you can only attach one finalizer per transaction and it should be attached before any DML operations occur in the transaction.
This feature, when utilized correctly, can greatly enhance the robustness and maintainability of Apex applications by separating the transactional logic from the post-transaction logic.
Example Scenario:
Consider a scenario where a Salesforce application processes incoming orders. When an order is placed, multiple asynchronous tasks are initiated, including order processing, inventory updates, and email notifications. To ensure data consistency, transaction finalizers are used to handle cleanup tasks in case of transaction failure.
public class OrderProcessing implements Queueable { public void execute(QueueableContext context) { // Process order logic // Update inventory // Send email notifications } } public class TransactionFinalizer implements Queueable { public void execute(QueueableContext context) { if (context.isSuccess()) { // Transaction was committed // Perform post-processing tasks // Log success message } else { // Transaction was rolled back // Perform cleanup tasks // Log error message } } } public class OrderService { public static void placeOrder(Order order) { // Enqueue order processing job System.enqueueJob(new OrderProcessing()); // Enqueue transaction finalizer System.enqueueJob(new TransactionFinalizer()); } }
Explanation:
- OrderProcessing Class: Represents the asynchronous job responsible for processing the order. It contains the main business logic for handling orders, updating inventory, and sending notifications.
- TransactionFinalizer Class: Implements the logic to be executed after the transaction is committed or rolled back. The
execute
method checks whether the transaction was successful using theisSuccess()
method provided by theQueueableContext
interface. Depending on the transaction outcome, it performs appropriate cleanup or post-processing tasks. - OrderService Class: Provides a method
placeOrder
to initiate the order processing workflow. It enqueues both theOrderProcessing
job and theTransactionFinalizer
job within the same transaction context.
Conclusion:
Transaction finalizers in Queueable Apex play a vital role in ensuring transaction integrity and data consistency in Salesforce applications. By allowing developers to execute cleanup or post-processing tasks after the transaction is completed, transaction finalizers help maintain data reliability even in complex asynchronous processing scenarios. Developers should leverage transaction finalizers judiciously to handle critical tasks and maintain application robustness.