I'm trying to understand Salesforce's locking mechanism. I ran a test which does the following:

  1. Calls Database.setSavepoint().
  2. Issues a Select for Update query.
  3. Calls Database.rollback(savepoint).
  4. Delays for several seconds.
  5. Exits the transaction.

I expected other threads to be able to access the record once step 3 competed. What I see is that while the changes to the record are rolled back by step 3, other threads cannot access the record until after step 5. In other words, while the changes may be rolled back, the locks aren't released.

Is this understanding correct? Is there any way for the locks to be released by the rollback?


There are no threads in Apex and transaction demarcation in SF is Sort of Squishy™. The acquisition of the lock by SELECT..FOR UPDATE is done in the context of a transaction. This transaction happens to be the "parent" of the transaction(s) performed by one or more savepoints. The lock is released when this parent transaction ends...but calling rollback on a savepoint does not affect the parent transaction.

Savepoint is usually implemented as a nested transaction. A transaction is started and within that transaction you can preserve the state of the database via a savepoint. You can do so multiple times. Each time you do it, a new nested transaction is started within the parent. When you roll back to a savepoint, the parent transaction is alive and well.

In SF's implementation this is easy to prove by considering the case of multiple savepoints:

Savepoint sp1 = Database.setSavepoint();
// do some work 

Savepoint sp2 = Database.setSavepoint();
// do some more work

If the first call to rollback ended the "parent" transaction (wherever it was started), the second call to rollback would have issues.

  • So, if I understand what you are saying, even if I'm in the context of nested transaction, the lock for a Select for Update is stored in the context of the highest level "parent" transaction rather than the nested transaction? That's unfortunate. The documentation of Database.rollback() says it "[r]estores the database to the state specified by the savepoint variable". Apparently that is not true as locks which were not part of the state at the time the savepoint was created now exist after the rollback is executed. – silverglass Sep 8 '20 at 12:41
  • Documentation should be improved, yes. Locks are special and behavior of a savepoint with respect to a row lock is a dark art even outside of Salesforce. MySQL/InnoDB: doesn't release locks. Postgres: does. Cockroach/Postgres: doesn't/does. Oracle: weird hybrid. – identigral Sep 8 '20 at 16:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.