3

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?

2 Answers 2

4

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 
Database.rollback(sp1);

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

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

2
  • 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. Commented Sep 8, 2020 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
    Commented Sep 8, 2020 at 16:25
2

Just in case someone finds this thread while researching the same question, in the Spring '24 release, Salesforce made a change to the behavior of DML when rolling back to savepoints. You can read the release notes entry here.

In summary, rolling back to a savepoint will now roll back all uncommitted DML. While the release notes do not address FOR UPDATE locks directly, in my tests they now are released in the rollback.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .