Transaction control statements in Apex are two methods on the Database class that let you snapshot a point in the transaction and roll back to it on failure:
Savepoint sp = Database.setSavepoint(); // mark a point
Database.rollback(sp); // undo every DML since that mark
That’s the whole API. Apex does not have free-standing SQL-style BEGIN/COMMIT/ROLLBACK — your transaction begins implicitly when the request enters Apex and commits when the request returns successfully. These two methods are the only knobs you get.
The vocabulary
- Savepoint — an opaque token, type
System.Savepoint, returned byDatabase.setSavepoint(). It represents a snapshot of the database state at that instant. - Rollback —
Database.rollback(savepoint)discards every DML change made after the savepoint was created.
What gets rolled back
| State | Rolled back? |
|---|---|
| INSERT/UPDATE/DELETE/UPSERT done after the savepoint | Yes — fully reverted |
| INSERT/UPDATE/DELETE done before the savepoint | No — retained |
| Record locks taken since the savepoint | Yes — released |
| Salesforce-assigned Ids on rolled-back inserts | Discarded (don’t reuse them) |
System.debug output | No (debug stream isn’t transactional) |
EventBus.publish of Publish-Immediately events | No — already published outside the tx |
EventBus.publish of Publish-After-Commit events | Yes — those are queued, never published |
| Governor-limit counters (SOQL, DML, CPU, heap) | No — these keep accumulating |
@future calls already enqueued in this tx | Yes — they’re discarded if the calling tx rolls back |
| Static variable values | No — Apex statics are not transactional state |
Multiple savepoints
You can take multiple savepoints in one transaction and roll back to any of them:
Savepoint sp1 = Database.setSavepoint();
insert acc1;
Savepoint sp2 = Database.setSavepoint();
insert acc2;
try {
insert acc3;
update orders;
} catch (Exception e) {
Database.rollback(sp2); // undo acc3 and orders, but keep acc2
}
Rolling back to sp1 would have undone acc2 as well. Savepoints aren’t strictly nested — they’re more like checkpoints on a timeline.
A cap on savepoints
A single transaction can hold up to five savepoints simultaneously. Setting a sixth invalidates the oldest one. This is rarely hit in practice — most code uses one or two.
What about pre-Apex transactions?
Two scenarios that look transactional but aren’t:
- HTTP callouts —
Http.send()is not transactional. If the callout succeeds and the Apex transaction later rolls back, the external system still saw the request. Idempotency keys are how you handle this in the real world. - Platform Events with “Publish Immediately” — sent outside the transaction. They survive a rollback. (This is sometimes a feature, sometimes a bug — depends on your intent.)
If you need an external action to roll back atomically with Apex, you usually can’t — that’s a distributed-systems problem. The pattern is to compensate (publish a “cancel” message) rather than try to roll back the external system.
Common antipatterns
- Forgetting to set a savepoint before risky DML — when the exception fires, there’s nothing to roll back to except the start of the transaction (which Apex does for you anyway).
- Setting a savepoint but never rolling back — if the operation succeeds, the savepoint is silently discarded at transaction end. Harmless, but signals confused intent.
- Rolling back to free governor budget — it doesn’t. SOQL/DML/CPU counters are not transactional.
- Using
try/catchinstead of a savepoint — catching an exception doesn’t undo the DML you already did before the catch block.
What interviewers are really looking for
The naming check: “Database.setSavepoint() and Database.rollback().” The senior signal includes: (1) Apex transactions are implicit — no BEGIN/COMMIT, (2) up to 5 savepoints in one tx, (3) rollback doesn’t free governor budget, (4) Ids from rolled-back inserts must be discarded, (5) callouts don’t roll back — you need compensating actions for cross-system consistency.
Verified against: Apex Developer Guide — Transaction Control, Database.rollback(). Last reviewed 2026-05-17.