10,000 Records Pointing to One Parent: Hello, Lock Errors
“Put 10,000 child records under one Account. Every bulk operation got UNABLE_TO_LOCK_ROW errors.”
What Happened
For Mobilization Funding, we had a 'General Pool' Account that was the default parent for all unassigned Funding Requests. Over time, 10,000+ records ended up under this one Account. Every time a batch job processed Funding Requests, it tried to lock the parent Account for rollup recalculations. Multiple batch threads fighting over the same parent record. UNABLE_TO_LOCK_ROW errors everywhere. We had to redistribute records across multiple pool Accounts and implement a round-robin assignment.
The Wrong Way
// "Just put them all under one Account" // // Account: General Pool (001xxxx) // └── 10,000+ Funding_Request__c records // // Batch job processes 200 at a time across // multiple threads. Each thread locks the parent // Account for rollup summaries. // // Thread 1: Lock Account 001xxxx → success // Thread 2: Lock Account 001xxxx → WAITING // Thread 3: Lock Account 001xxxx → WAITING // Thread 2: → UNABLE_TO_LOCK_ROW
The Right Way
// Distribute across multiple parent records
//
// Account: General Pool A (001aaa)
// └── 2,000 Funding_Request__c
// Account: General Pool B (001bbb)
// └── 2,000 Funding_Request__c
// Account: General Pool C (001ccc)
// └── 2,000 Funding_Request__c
//
// Round-robin assignment in trigger:
private static Integer poolCounter = 0;
private static List<Id> poolAccounts =
getPoolAccountIds(); // cached
public static Id getNextPoolAccount() {
Id selected = poolAccounts[
Math.mod(poolCounter, poolAccounts.size())
];
poolCounter++;
return selected;
}
// Keep each parent under 10K children.
// Salesforce docs call this "data skew."The Lesson
More than 10,000 child records under one parent causes lock contention in bulk operations. Distribute children across multiple parents. Salesforce calls this 'lookup skew' — learn it before it learns you.
Enjoyed this? Get more like it.
Glen's Musings — AI, investing, and building things. Occasional. Free.
More Data Mistakes
Mass Update With No Backup: The 'Undo' Button That Doesn't Exist
Ran a mass update on 50,000 records without exporting them first. Client asked to revert. I had nothing.
Read morePainfulNo Duplicate Rules: 14,000 Duplicate Accounts Later
Launched an org without duplicate matching rules. Users created duplicates for a year. Cleanup took three months.
Read morePainfulHard Delete When Soft Delete Would Do
Used Hard Delete in Data Loader. Records bypassed the Recycle Bin. Permanent deletion. No recovery.
Read more