UNABLE_TO LOCK_ROW.
Data skew on a hot parent. Concurrent updates stall waiting for a lock that never releases.
The problem.
System.DmlException: UNABLE_TO_LOCK_ROW
When one parent has tens of thousands of children, updates that touch the parent row serialize. Concurrent users hit UNABLE_TO_LOCK_ROW.
50k+
Child records on a skewed parent
Lock
Exclusive row access required
8 hrs
Average downtime from contention
Bad vs good.
ContactTriggerHandler.clsAnti-pattern
// ❌ BAD: Updating parent record when children have data skew
// Top Account has 50,000+ Contacts (data skew)
public void updateAccountIndustry(List<Contact> contacts) {
Set<Id> accountIds = new Set<Id>();
for (Contact c : contacts) {
accountIds.add(c.AccountId);
}
// Lock contention on Account with 50,000 children
List<Account> accounts = [
SELECT Id, Industry
FROM Account
WHERE Id IN :accountIds
FOR UPDATE // ❌ Lock wait timeout!
];
for (Account acc : accounts) {
acc.Industry = 'Technology';
}
update accounts;
}ContactTriggerHandler.clsFixed
// ✅ GOOD: Avoid locking skewed parent records
// Use selective updates without parent locking
public void updateAccountIndustry(List<Contact> contacts) {
Set<Id> accountIds = new Set<Id>();
for (Contact c : contacts) {
accountIds.add(c.AccountId);
}
// Update without explicit lock
List<Account> accounts = [
SELECT Id, Industry
FROM Account
WHERE Id IN :accountIds
// No FOR UPDATE - let Salesforce handle locking
];
// Batch updates to reduce contention
Database.update(accounts, false); // Partial success allowed
// Or: Use async processing for high-volume updates
// System.enqueueJob(new AccountUpdateJob(accounts));
}The fix.
Avoid updating skewed parent fields from child triggers. Use asynchronous aggregation or denormalized counters carefully. Jataka analyzes parent-child ratios before merge.
Catch this before production.
Book a pilot and watch Jataka block this exact anti-pattern on your next PR.