Skip to main content

SF-0281 · Concept · Hard

What is a record locking or locking statement in salesforce?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Record locking is the database’s way of guaranteeing that one transaction can update a row without another transaction sneaking in and overwriting its changes. In Apex, you request a lock explicitly by appending FOR UPDATE to a SOQL query. From that moment until the transaction commits or rolls back, no other transaction can update those records — they queue up and wait.

The locking statement

// Acquires row-level locks on the matching accounts
List<Account> accounts = [
    SELECT Id, Name, Total_Spend__c
    FROM Account
    WHERE Id IN :accountIds
    FOR UPDATE
];

// Safe to read and mutate — no other transaction can update these rows
for (Account a : accounts) {
    a.Total_Spend__c += newAmount;
}
update accounts;
// Lock released when the transaction commits (here, at end of method)

The FOR UPDATE clause must appear at the end of the SOQL statement. It cannot be combined with ORDER BY (the platform throws a compile error if you try).

When locking actually matters

The textbook example is a counter or aggregate updated by multiple sources concurrently:

Time   Tx A                            Tx B
0      Read Account.Total = 100
1                                      Read Account.Total = 100
2      Compute new = 100 + 50 = 150
3                                      Compute new = 100 + 25 = 125
4      Update Total = 150
5                                      Update Total = 125  ← A's increment lost

Both transactions read 100, both wrote independently, and the second write clobbered the first. The customer’s Total_Spend__c is now 125 instead of the correct 175. This is called a lost update, and it’s exactly what FOR UPDATE prevents:

Time   Tx A                            Tx B
0      SELECT ... FOR UPDATE → locks
1                                      SELECT ... FOR UPDATE → waits
2      Update Total = 150
3      Commit (lock released)
4                                      Now reads Total = 150
5                                      Update Total = 175  ← correct

What the lock covers and what it costs

  • Scope: row-level locks on every record the SOQL returns. Other transactions can still read those rows (FOR UPDATE is non-blocking for readers) but cannot update or delete them until your transaction commits.
  • Duration: lasts until the transaction ends. Apex transactions are short, but a 60-second batch chunk holding locks on hot rows can serialize a lot of contending work.
  • Lock timeout: 10 seconds. If your FOR UPDATE query can’t acquire the lock within 10 seconds, the platform throws QueryException: UNABLE_TO_LOCK_ROW. You have to handle this — usually by retrying or queueing the operation.
try {
    List<Account> accounts = [
        SELECT Id FROM Account WHERE Id IN :ids FOR UPDATE
    ];
    // ... mutate ...
} catch (QueryException e) {
    if (e.getMessage().contains('UNABLE_TO_LOCK_ROW')) {
        // Re-queue for later
        System.enqueueJob(new RetryAccountUpdate(ids));
    } else {
        throw e;
    }
}

When to use it (and when not)

Use FOR UPDATE when:

  • Multiple async paths can update the same record concurrently (e.g., an inbound integration plus a scheduled aggregation).
  • You read a value, compute on it, and write back — the classic read-modify-write that’s vulnerable to lost updates.
  • You’re processing a parent record whose children you’ll bulk-insert (lock the parent so its rollup runs once).

Avoid it when:

  • The operation is genuinely idempotent (setting a flag to true either way).
  • The record is rarely contended — the overhead and lock-timeout risk isn’t worth it.
  • You’re locking a hot row that every transaction needs (you’ll serialize the org).

Implicit locks

FOR UPDATE isn’t the only path to a lock. The platform takes implicit locks during:

  • DML on a record — the row is locked for the rest of the transaction.
  • Parent record updates from a child DML — inserting a child can lock the parent (master-detail relationships in particular).
  • Sharing-rule recalculation — large ownership changes acquire broad locks.

These implicit locks are the most common source of UNABLE_TO_LOCK_ROW in production code that never uses FOR UPDATE.

What interviewers are really looking for

Locking is a senior-level topic. The hiring signal is whether you understand why the platform exposes FOR UPDATE: it’s not for performance, it’s for correctness in the face of concurrency. Mention the lost-update problem, the 10-second timeout, and the difference between explicit and implicit locks, and you’ve shown the depth they’re checking for.

Verified against: Apex Developer Guide — Locking Statements, SOQL FOR UPDATE. Last reviewed 2026-05-17 for Spring ‘26 release.