Background: Governor Limits in Salesforce
Why Limits Exist
Governor limits ensure multi-tenant stability across the Salesforce platform. While synchronous contexts have well-documented boundaries, asynchronous contexts (e.g., Queueable, Batchable, Future) operate under different ceilings, which developers often overlook.
Examples of Common Async Limits
- Queueable Chain Depth: Max 50
- CPU Time Limit: 60,000 ms (async)
- DML Rows: 10,000
- SOQL Queries: 200
- Callouts: 100
Symptoms and Red Flags
Inconsistent Failures
Jobs may succeed sporadically and fail under load or certain data conditions. Error logs may show generic "System.LimitException" messages with no precise stack trace.
Unprocessed or Stuck Queueables
Chained Queueables silently fail beyond the 50-level chain depth. Scheduled jobs may not enqueue due to unhandled platform exceptions.
Root Causes and Diagnostic Approach
1. Unbounded Chaining of Queueables
Recursive business logic, especially in data-driven job chaining, causes exponential queue growth. This breaches the platform's max chain depth limit.
public class MyQueueable implements Queueable { public void execute(QueueableContext context) { // dangerous if called conditionally without guard System.enqueueJob(new MyQueueable()); } }
2. Large Data Volume (LDV) without Batching
Running DML or SOQL on LDV without batching or selective queries causes heap size or CPU time violations in async contexts.
3. Improper Future Call Nesting
Future methods invoking other futures or DML operations exceeding limits silently break flows without clear diagnostics.
Step-by-Step Fix: Hardening Async Workflows
1. Audit Async Chain Depth
- Use static counters in custom metadata or job context
- Avoid blind chaining; design workflows to fan out instead of chaining deeply
2. Switch to Batchable Where Necessary
Batchable jobs handle larger volumes and support stateful iteration with built-in chunking.
global class AccountBatch implements Database.Batchable<SObject> { global Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator('SELECT Id FROM Account'); } global void execute(Database.BatchableContext bc, List<SObject> scope) { // process accounts in batch } global void finish(Database.BatchableContext bc) {} }
3. Use Limits Class for Runtime Checks
Track limits dynamically to exit early before hitting ceilings.
if (Limits.getCPUTime() > 55000) return; if (Limits.getLimitDmlRows() - Limits.getDmlRows() < 100) return;
4. Instrument and Log Execution Paths
- Log CPU time, heap usage, and SOQL/DML count per async entry
- Correlate logs with Job Ids via Platform Events or Custom Objects
Architectural Recommendations
Idempotency and Guardrails
Ensure async operations are idempotent and guarded by context tokens or record status flags. Avoid enqueueing duplicate jobs on the same record.
Design for Fan-Out Not Chain-In
Break workflows into multiple short-lived, parallelizable jobs rather than sequential chains. Use Platform Events or Change Data Capture for pub-sub patterns.
Monitor via Event Monitoring or Shield
Leverage Salesforce Shield or Event Monitoring logs to trace high-failure job types and preempt limit breaches through proactive alerting.
Conclusion
Async governor limit violations in Salesforce are rarely simple bugs; they are often signs of architectural debt. Addressing them requires not just code corrections but redesigning async orchestration patterns. With proper batching, monitoring, and design discipline, even complex automations can run reliably at scale in Salesforce Cloud.
FAQs
1. Why do Queueable jobs silently fail after chaining?
Salesforce enforces a hard chain depth limit of 50 for Queueables. Beyond that, enqueueJob() returns null silently without an exception.
2. How can I debug asynchronous failures in Salesforce?
Use the Apex Jobs page, Debug Logs with Fine-grained filters, and implement platform-level instrumentation like custom logging and Platform Events.
3. Is it better to use Batchable or Queueable for large operations?
Batchable is more suitable for LDV and supports chunking and stateful processing, making it ideal for large, complex jobs over Queueable.
4. Can governor limits vary across orgs?
No, governor limits are consistent across all orgs. However, features like Shield, Event Monitoring, or async monitoring tools may vary by license tier.
5. What tools help with proactive detection of async issues?
Salesforce Shield, Event Monitoring, and custom logging frameworks with Platform Events offer real-time visibility into async failures and trends.