Managing large-scale data processing in Salesforce requires balancing high data volumes with strict multi-tenant governor limits. While synchronous transactions must complete within milliseconds, Batch Apex handles millions of records asynchronously. The Apex Flex Queue is the core platform infrastructure that governs how these batch jobs are queued, prioritized, and executed without failing due to concurrency constraints.
1. How the Apex Flex Queue Operates
Prior to the Flex Queue, submitting a batch job when all processing threads were full resulted in immediate job rejection. The Flex Queue eliminates this limitation by introducing the Holding status:
- Holding Status: When you call
Database.executeBatch(), the job enters the Flex Queue in theHoldingstate if all 5 concurrent execution slots are occupied. - Up to 100 Queued Jobs: The Flex Queue can hold up to 100 batch jobs simultaneously in the
Holdingstate per organization. - Automatic Dispatching: As running jobs complete or fail, Salesforce automatically pulls jobs from the top of the Flex Queue and transitions their status from
HoldingtoQueued, thenPreparing, and finallyProcessing.
- Concurrent Processing Limit: Exactly 5 Batch Apex jobs can run simultaneously in
ProcessingorPreparingstatus. - Flex Queue Capacity: Holds up to 100 batch jobs in
Holdingstatus. - Daily 24-Hour Execution Limit: 250,000 asynchronous executions or 200 × total user licenses (whichever is greater).
- Job Reordering Support: Jobs in the
Holdingstatus can be reordered programmatically or declaratively; active jobs cannot.
2. Programmatic Reordering with the System.FlexQueue Class
Salesforce provides the System.FlexQueue class, allowing developers to dynamically adjust the execution order of queued batch jobs based on business urgency (e.g., pushing critical nightly billing runs ahead of general data cleanups).
FlexQueue.moveBeforeJob(jobToMoveId, targetJobId): Moves a holding job directly ahead of another holding job in the queue.FlexQueue.moveAfterJob(jobToMoveId, targetJobId): Places a holding job directly after another specified holding job.FlexQueue.moveJobToFront(jobId): Promotes a holding job to Position 1 so it becomes the next job to execute.FlexQueue.moveJobToEnd(jobId): Deprioritizes a holding job by sending it to the back of the queue.
Submitting an invoice generation batch and promoting it to the front of the Flex Queue:
public with sharing class InvoiceBatchManager {
public static void submitPriorityBillingRun() {
// 1. Submit Batch Job
DailyInvoiceBatch batchInstance = new DailyInvoiceBatch();
Id batchJobId = Database.executeBatch(batchInstance, 200);
// 2. Check if the job was placed in the Holding state
AsyncApexJob jobInfo = [
SELECT Id, Status, JobType
FROM AsyncApexJob
WHERE Id = :batchJobId
WITH USER_MODE
LIMIT 1
];
// 3. Promote to Position 1 if currently in the Flex Queue
if (jobInfo.Status == 'Holding') {
Boolean isMoved = System.FlexQueue.moveJobToFront(batchJobId);
if (isMoved) {
System.debug(LoggingLevel.INFO, 'Batch job successfully prioritized to Position 1 in Flex Queue: ' + batchJobId);
}
}
}
}
3. Declarative Management & UI Monitoring
Administrators and release managers can monitor and reorder holding jobs directly in Salesforce Setup without writing code:
- Navigate to Setup > in the Quick Find box, enter Apex Flex Queue.
- Click Apex Flex Queue to view all batch jobs currently in the
Holdingstatus. - Use the Reorder action buttons (or drag-and-drop handles) to move critical jobs to higher positions.
- To monitor actively running and completed jobs, navigate to Setup > Apex Jobs.
4. Common Developer Traps & Best Practices
Calling
System.FlexQueue.moveJobToFront() on a job whose status has already transitioned to Queued, Preparing, or Processing will return false and have no effect. Additionally, attempting to submit a batch job when the Flex Queue already contains 100 holding jobs throws a fatal System.AsyncException: Too many batch jobs in the queue.
System.FlexQueue reordering methods on jobs confirmed to be in Holding status.
- Check Queue Capacity Proactively: Query
[SELECT count() FROM AsyncApexJob WHERE Status = 'Holding']before enqueueing non-urgent data processing jobs. - Choose the Right Asynchronous Tool: Use Batch Apex when processing large tables exceeding 50,000 records; use Queueable Apex for sequential job chaining, external REST callouts, and smaller asynchronous workloads.
- Size Batch Chunks Appropriately: The default batch size is 200 records. For lightweight records, increase the chunk size up to 2,000 to process data faster; for heavy trigger chains, reduce chunk size to prevent CPU timeout exceptions.
Summary
The Apex Flex Queue provides a scalable buffer for high-volume Salesforce data processing. By understanding the 5-job concurrent execution cap, taking advantage of the 100-job holding capacity, and using the System.FlexQueue class to prioritize mission-critical tasks, you can design resilient, automated architectures that process enterprise workloads efficiently.