record-locking-and-contention

Use when diagnosing or preventing UNABLE_TO_LOCK_ROW errors, record lock contention in Apex or Bulk API loads, FOR UPDATE locking, parent-child lock escalation, and deadlock scenarios. Triggers: 'UNABLE_TO_LOCK_ROW', 'record lock contention', 'FOR UPDATE SOQL', 'deadlock', 'lock timeout'. NOT for approval-process record locking (admin/approval-processes) or optimistic concurrency via timestamps.

Best use case

record-locking-and-contention is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when diagnosing or preventing UNABLE_TO_LOCK_ROW errors, record lock contention in Apex or Bulk API loads, FOR UPDATE locking, parent-child lock escalation, and deadlock scenarios. Triggers: 'UNABLE_TO_LOCK_ROW', 'record lock contention', 'FOR UPDATE SOQL', 'deadlock', 'lock timeout'. NOT for approval-process record locking (admin/approval-processes) or optimistic concurrency via timestamps.

Teams using record-locking-and-contention should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/record-locking-and-contention/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/apex/record-locking-and-contention/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/record-locking-and-contention/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How record-locking-and-contention Compares

Feature / Agentrecord-locking-and-contentionStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when diagnosing or preventing UNABLE_TO_LOCK_ROW errors, record lock contention in Apex or Bulk API loads, FOR UPDATE locking, parent-child lock escalation, and deadlock scenarios. Triggers: 'UNABLE_TO_LOCK_ROW', 'record lock contention', 'FOR UPDATE SOQL', 'deadlock', 'lock timeout'. NOT for approval-process record locking (admin/approval-processes) or optimistic concurrency via timestamps.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Record Locking and Contention

Use this skill when transactions fail with UNABLE_TO_LOCK_ROW or when high-concurrency workloads contend on the same rows. The objective is to identify the locking mechanism involved, eliminate unnecessary contention, and add resilient retry logic where contention is unavoidable.

---

## Before Starting

Gather this context before working on anything in this domain:

- What error is surfacing: UNABLE_TO_LOCK_ROW, lock timeout, or silent transaction rollback?
- Is contention driven by Bulk API parallel batches, concurrent user actions, scheduled jobs, or platform events?
- Does the data model have parent-child skew where a single parent record has thousands of children?

---

## Core Concepts

### Exclusive Row Locks and the 10-Second Timeout

Every DML statement in Salesforce acquires an exclusive row lock on the affected records for the duration of the transaction. If a second transaction attempts to lock the same row, it waits up to 10 seconds. After that timeout, the platform throws `UNABLE_TO_LOCK_ROW` and the waiting transaction fails. This is not configurable.

### FOR UPDATE Acquires Explicit Locks in SOQL

Adding `FOR UPDATE` to a SOQL query acquires exclusive locks on the returned rows immediately, before any DML occurs. This is useful for read-modify-write patterns where you need to guarantee no other transaction changes the row between your read and your write. However, FOR UPDATE locks persist for the entire transaction, so long-running logic after the query extends the lock window and increases contention risk.

### Parent-Child Lock Escalation

When you insert, update, or delete a child record, Salesforce also locks the parent record to maintain rollup summaries, sharing calculations, and relationship integrity. This means concurrent child-record operations against the same parent contend on the parent row even though no two transactions touch the same child. This is the primary cause of lock contention in orgs with data skew.

### Deadlocks from Inconsistent Lock Ordering

When two transactions lock the same set of rows in different orders, a deadlock can occur. Transaction A holds Row 1 and waits for Row 2; Transaction B holds Row 2 and waits for Row 1. The platform detects this and fails one transaction with UNABLE_TO_LOCK_ROW. Consistent lock ordering by a deterministic key (such as record ID) prevents this.

---

## Common Patterns

### Sort-Before-DML to Prevent Deadlocks

**When to use:** Any batch or bulk operation that processes records from multiple parent groups or touches rows that other concurrent transactions may also touch.

**How it works:** Sort the records by a deterministic key (parent ID, or record ID) before performing DML. This guarantees all concurrent transactions acquire locks in the same order, eliminating deadlock risk.

```apex
// Sort by AccountId before update to prevent deadlock
List<Contact> contacts = [SELECT Id, AccountId, LastName FROM Contact WHERE ...];
contacts.sort(); // Custom comparator on AccountId
update contacts;
```

**Why not the alternative:** Processing records in arbitrary order is the root cause of most deadlocks in parallel batch operations.

### Retry via Queueable for Transient Lock Failures

**When to use:** When lock contention is occasional and unavoidable, such as high-volume integrations writing to shared parent records.

**How it works:** Catch `UNABLE_TO_LOCK_ROW` in a try-catch block and enqueue a Queueable to retry the failed subset of records after a short delay.

```apex
public class RetryableUpdate implements Queueable {
    private List<SObject> records;
    private Integer retryCount;

    public RetryableUpdate(List<SObject> records, Integer retryCount) {
        this.records = records;
        this.retryCount = retryCount;
    }

    public void execute(QueueableContext ctx) {
        try {
            update records;
        } catch (DmlException e) {
            if (e.getMessage().contains('UNABLE_TO_LOCK_ROW') && retryCount < 3) {
                System.enqueueJob(new RetryableUpdate(records, retryCount + 1));
            } else {
                throw e; // Exhausted retries or different error
            }
        }
    }
}
```

**Why not the alternative:** Retrying synchronously in a loop wastes governor limits and still contends on the same lock window. Queueable retry introduces a natural delay.

### Bulk API Serial Mode for Skewed Data

**When to use:** Bulk API loads that target child objects with high parent-record skew (such as loading Contacts where many share the same Account).

**How it works:** Sort the CSV by parent ID and set the Bulk API job to serial mode. Serial mode processes one batch at a time, so no two batches contend on the same parent row simultaneously.

**Why not the alternative:** Parallel mode is faster but creates multiple batches that can lock the same parent row concurrently, causing widespread UNABLE_TO_LOCK_ROW failures.

---

## Decision Guidance

| Situation | Recommended Approach | Reason |
|---|---|---|
| Bulk API loads failing on child objects with shared parents | Sort CSV by parent ID and use serial mode | Eliminates parallel contention on parent rows |
| Apex batch job with intermittent lock errors | Sort scope records by parent key before DML | Prevents deadlock from inconsistent ordering |
| High-frequency integrations writing to shared records | Catch UNABLE_TO_LOCK_ROW and retry via Queueable | Transient contention resolves with a short delay |
| Extreme data skew (10,000+ children per parent) | Request Granular Locking from Salesforce Support | Prevents child DML from escalating to parent lock |
| Read-modify-write requiring consistency | Use FOR UPDATE but minimize post-query logic | Reduces the lock-hold window |
| Multiple objects updated in one transaction | Lock rows in consistent ID order across all transactions | Prevents deadlocks |

---

## Recommended Workflow

Step-by-step instructions for an AI agent or practitioner working on this task:

1. Identify the contention source — examine error logs for UNABLE_TO_LOCK_ROW, note which object and operation triggers it, and determine whether the cause is parallel batches, parent-child escalation, or concurrent user actions.
2. Map the data model — identify parent-child relationships involved and check for data skew (a single parent with thousands of children).
3. Check lock ordering — review Apex code and batch logic to confirm records are processed in a deterministic, consistent order across all concurrent transactions.
4. Apply the appropriate pattern — sort-before-DML for deadlocks, serial mode for Bulk API skew, Queueable retry for transient failures, or Granular Locking for extreme skew.
5. Validate — run the checker script against the codebase, load-test with concurrent transactions, and confirm UNABLE_TO_LOCK_ROW errors no longer appear in the target scenario.

---

## Review Checklist

Run through these before marking work in this area complete:

- [ ] The specific lock contention source is identified (parent-child, parallel batch, concurrent DML, FOR UPDATE).
- [ ] Records are sorted by a deterministic key before DML in batch and bulk operations.
- [ ] FOR UPDATE queries are followed by minimal logic to reduce the lock-hold window.
- [ ] Retry logic exists for scenarios where transient lock contention is unavoidable.
- [ ] Bulk API jobs targeting skewed data use serial mode with sorted input.
- [ ] Data skew is assessed and Granular Locking is considered for extreme cases.
- [ ] No code acquires locks in inconsistent order across concurrent execution paths.

---

## Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

1. **Child DML locks the parent row** — inserting or updating a child record also acquires an exclusive lock on the parent. This is invisible in code but is the leading cause of contention in skewed data models.
2. **FOR UPDATE locks persist for the entire transaction** — if you run expensive logic after a FOR UPDATE query, you hold locks far longer than necessary, creating a wide contention window for other transactions.
3. **The 10-second lock timeout is not configurable** — you cannot extend or shorten it. If your transaction holds a lock for more than 10 seconds, any concurrent transaction waiting on the same row will fail.
4. **Aggregate SOQL on locked rows also blocks** — a SOQL query with GROUP BY or aggregate functions on rows that another transaction has locked will wait and potentially time out, even though no DML is involved.

---

## Output Artifacts

| Artifact | Description |
|---|---|
| Lock contention root cause analysis | Identifies the locking mechanism, contention source, and data-skew factors |
| Lock ordering recommendation | Deterministic sort strategy for DML operations to prevent deadlocks |
| Retry strategy | Queueable-based retry pattern for transient UNABLE_TO_LOCK_ROW errors |
| Bulk API configuration guidance | Serial-mode and CSV-sorting recommendations for skewed data loads |

---

## Related Skills

- `apex/batch-apex-patterns` — use when lock contention arises from batch Apex scope processing and requires batch-level mitigations.
- `apex/apex-queueable-patterns` — use when implementing retry logic via Queueable chaining for lock-failure recovery.
- `data/bulk-api-and-large-data-loads` — use when lock contention is driven by Bulk API job configuration rather than Apex code.
- `admin/data-skew-and-sharing-performance` — use when the root cause is data-model skew rather than code-level locking patterns.

Related Skills

record-access-troubleshooting

8
from PranavNagrecha/AwesomeSalesforceSkills

Diagnose why a user can or cannot see/edit a record: UserRecordAccess SOQL, Why Can a User Access This Record debug log, OWD, role hierarchy, sharing rules, manual/team/apex shares, implicit parent share. NOT for field-level security (use field-level-security-audit). NOT for designing sharing (use sharing-selection decision tree).

lwc-record-picker

8
from PranavNagrecha/AwesomeSalesforceSkills

lightning-record-picker base component (Winter '24 GA): object/record filter, displayInfo/matchingInfo, graph-ql filters, accessibility. Replaces ad-hoc lookup inputs. NOT for multi-select custom pickers (use lwc-multi-select-lookup). NOT for external-object lookup (use lwc-external-lookup).

lwc-lightning-record-forms

8
from PranavNagrecha/AwesomeSalesforceSkills

Lightning Data Service form components for LWC — when to use lightning-record-form vs lightning-record-edit-form vs lightning-record-view-form, output-field vs input-field, density modes, layout types (Compact/Full), and the platform-managed validation/save/error UI. NOT for fully custom form layouts (use lwc/lwc-custom-form-with-uiRecordApi) or aura:recordEditForm (Aura is deprecated for new work).

record-triggered-flow-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or reviewing Salesforce record-triggered Flows, especially before-save vs after-save behavior, entry criteria, recursion avoidance, and when to escalate to Apex. Triggers: 'before save vs after save', '$Record__Prior', 'record-triggered flow', 'order of execution', 'flow recursion'. NOT for screen-flow UX or pure bulkification work when the trigger model is already correct.

flow-record-save-order-interaction

8
from PranavNagrecha/AwesomeSalesforceSkills

Reason about how record-triggered flows interleave with the Salesforce Save Order (validation, before-save flows, before triggers, duplicate rules, after-save flows, workflow, after triggers, assignment, auto-response, escalation). Trigger keywords: save order, before-save flow, after-save flow, dml order, trigger vs flow order. Does NOT cover writing trigger handlers, approval process setup, or workflow rule migration.

flow-record-locking-and-contention

8
from PranavNagrecha/AwesomeSalesforceSkills

Diagnose and prevent UNABLE_TO_LOCK_ROW + parent-record contention in record-triggered, scheduled, and screen flows by mapping the implicit lock chain and applying decouple patterns (Platform Events, Queueable handoff, Scheduled Paths). NOT for general flow bulkification — see flow-bulkification. NOT for fault-path catch logic — see flow-rollback-patterns.

flow-get-records-optimization

8
from PranavNagrecha/AwesomeSalesforceSkills

Optimize Get Records elements in Flow: filter sharpness, field selection, sort-and-limit placement, caching via formula resources, and avoiding repeated queries in loops. Trigger keywords: get records, flow soql, flow query limit, flow performance, record lookup. Does NOT cover Apex SOQL, Data Cloud queries, or external object lookups.

record-merge-implications

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when merging Account, Contact, or Lead records in Salesforce and needing to understand what data is kept, what is deleted, and what side effects occur on related records. Triggers: 'which fields win in a merge', 'child records after merge', 'merge duplicate accounts', 'what happens to opportunities after contact merge', 'Lead merge field resolution'. NOT for deduplication strategy design (use data-quality-and-deduplication), NOT for Apex Merge DML beyond its direct implications.

architecture-decision-records

8
from PranavNagrecha/AwesomeSalesforceSkills

Author and maintain Architecture Decision Records (ADRs) for Salesforce implementations: capture chosen approach, rejected alternatives, constraints, and consequences. Trigger keywords: adr, architecture decision record, design decision log, technical decision. Does NOT cover project roadmap planning, release notes, or RFC workflow for features.

apex-record-clone-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

SObject.clone(preserveId, isDeepClone, preserveReadonly, preserveAutonumber): shallow vs deep clone semantics, related-record replication, clone with parent repointing, autonumber preservation. NOT for data migration (use bulk-api-and-large-data-loads). NOT for record snapshots (use field-history-tracking).

record-types-and-page-layouts

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing, auditing, or simplifying Record Types and Page Layouts. Triggers: 'record type', 'page layout', 'different picklist values', 'different fields per team', 'dynamic forms'. NOT for sharing rules or FLS — record types don't control data access.

record-type-strategy-at-scale

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or refactoring record types across objects with many profiles, business processes, or picklist variations. Covers layout assignment explosion, Dynamic Forms migration, and record type ID portability. NOT for basic record type setup or page layout assignment — see record-types-and-page-layouts for introductory guidance.