

Transaction Savepoints and Rollback Strategies in Apex
Why Do We Need Savepoints? Imagine you’re booking concert tickets online. You select your seats, enter payment details, and hit “Confirm.” But what if the payment fails after the seats are marked as reserved? Without a way to undo the seat reservation, those seats might stay locked, frustrating other customers. This is where transaction savepoints come to the rescue. They let you undo specific parts of a transaction if errors occur, ensuring data stays consistent. Join us to learn about Transaction Savepoints and Rollback Strategies in Apex.
In this blog, we’ll explore how to use savepoints and rollbacks in Apex with a reimagined real-world example and detailed code explanations.
How Savepoints Work in Salesforce
Key Terms Simplified:
Transaction: A group of actions (like updating records) that should all succeed or fail together.
Savepoint: A “checkpoint” in your transaction. If something goes wrong later, you can undo everything after this point.
Rollback: Reverting changes made after a savepoint, like pressing an “undo” button.
Basic Syntax:
// Step 1: Create a savepoint (like saving your game progress)
Savepoint sp = Database.setSavepoint();
// Step 2: Perform database operations (insert/update/delete records)
// Step 3: If an error occurs, rollback to the savepoint
Database.rollback(sp);
Scenario 1: Event Ticket Reservation System
Problem: A customer tries to reserve 3 seats for a concert. The system must:
- Check seat availability.
- Reserve the seats.
- Charge the customer’s credit card.
If any step fails (e.g., seats already taken, payment declined), all changes should be undone.
Code Example:
// Step 1: Create a savepoint to roll back to if anything fails
Savepoint sp = Database.setSavepoint();
try {
// Step 2: Check seat availability
List<Concert_Seat__c> seats = [SELECT Id, Status__c FROM Concert_Seat__c WHERE Seat_Number__c IN :selectedSeatIds];
// Step 3: Reserve seats (if they're available)
for (Concert_Seat__c seat : seats) {
if (seat.Status__c != 'Available') {
throw new ReservationException('Seat ' + seat.Seat_Number__c + ' is already taken!');
}
seat.Status__c = 'Reserved';
}
update seats; // Update seats to "Reserved"
// Step 4: Charge the customer (simulated payment gateway call)
PaymentResult result = PaymentGateway.chargeCustomer(customerId, totalAmount);
if (!result.isSuccess) {
throw new PaymentException('Payment failed: ' + result.errorMessage);
}
} catch (Exception e) {
// Step 5: Rollback ALL changes if any error occurs
Database.rollback(sp);
System.debug('Error: ' + e.getMessage());
// Notify the customer (e.g., email, UI message)
}
Detailed Code Explanation:
1. Savepoint Creation:
- Savepoint sp = Database.setSavepoint();
- This marks the start of our transaction. If anything fails later, we can undo changes back to this point.
2. Seat Availability Check:
- We query seats using selectedSeatIds (e.g., [“Seat-A1”, “Seat-A2”]).
- If any seat is not Available, we throw a custom exception (ReservationException).
3. Reserving Seats:
- If all seats are available, we update their Status__c to Reserved.
- update seats; sends this change to the database.
4. Payment Processing:
- We simulate calling a payment gateway (e.g., Stripe).
- If the payment fails (e.g., insufficient funds), we throw a PaymentException.
5. Rollback on Failure:
- The catch block triggers a rollback using Database.rollback(sp);.
- This undoes the seat reservation (sets Status__c back to Available).
Note: The customer is NOT charged because the payment step failed, and Salesforce automatically rolls back the seat reservation
Real-World Impact:
Without Savepoints: If the payment fails after seats are reserved, those seats stay “Reserved” but unpaid, blocking other customers.
With Savepoints: Seats are automatically released if the payment fails, improving customer experience and preventing lost sales.
Scenario 2: Partial Rollbacks in Order Returns
Problem: A customer returns a product. The system must:
- Refund the payment.
- Restock the item.
- Log the return in an audit table.
Even if restocking fails, the refund should still process, but the audit log must always be saved.
Code Example:
// Savepoint for the entire transaction
Savepoint mainSP = Database.setSavepoint();
try {
// Step 1: Refund payment (external API call)
RefundResult refund = RefundService.processRefund(orderId);
if (!refund.isSuccess) {
throw new RefundException('Refund failed: ' + refund.error);
}
// Step 2: Create a nested savepoint before restocking
Savepoint restockSP = Database.setSavepoint();
try {
// Restock the product
Product__c product = [SELECT Id, Stock__c FROM Product__c WHERE Id = :productId];
product.Stock__c += 1;
update product;
} catch (Exception e) {
// Rollback ONLY the restock attempt, keep the refund
Database.rollback(restockSP);
System.debug('Restock failed: ' + e.getMessage());
}
// Step 3: Log the return (MUST always save, even if restocking fails)
Return_Audit__c audit = new Return_Audit__c(
Order__c = orderId,
Message__c = 'Return processed with partial restock error'
);
insert audit;
} catch (Exception e) {
// Rollback EVERYTHING (refund, restock, audit) if refund fails
Database.rollback(mainSP);
}
Key Takeaways:
- Nested Savepoints: Use multiple save points to control what gets rolled back. Here, the refund is preserved even if restocking fails.
- Audit Logging: Critical actions (like insert audit) should be placed after risky operations to ensure they’re saved.
Best Practices for Savepoints
- Limit Savepoints: Salesforce allows 5 save points per transaction. Don’t waste them!
- Order Matters: Place savepoints before risky operations (e.g., callouts, updates).
- Test Thoroughly: Use Test.startTest() and Test.stopTest() to simulate errors in unit tests.
- Avoid DML After Rollback: Rollbacks reset record IDs. Re-insert records if needed.
Final Thoughts
Savepoints are like a safety net for your transactions. They ensure your data stays consistent, even when things go wrong. Whether you’re reserving tickets, processing payments, or handling returns, they’re a must-have tool for building resilient Salesforce apps.