subscribe our youtube channel popup

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:

  1. Check seat availability.
  2. Reserve the seats.
  3. 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:

  1. Refund the payment.
  2. Restock the item.
  3. 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:

  1. Nested Savepoints: Use multiple save points to control what gets rolled back. Here, the refund is preserved even if restocking fails.
  2. 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.

Satyam parasa
Satyam parasa

Satyam Parasa is a Salesforce and Mobile application developer. Passionate about learning new technologies, he is the founder of Flutterant.com, where he shares his knowledge and insights.

Articles: 44

Leave a Reply

Your email address will not be published. Required fields are marked *