Asynchronous processing increases scalability by allowing higher limits per context. Asynchronous requests execute in their own threads behind the scenes, so users can continue to do work on the client side as the asynchronous tasks execute in the background.
Consider an asynchronous ordering process in Salesforce. When an order is saved, it triggers a message to an external warehouse management system with special instructions for how to pack and ship an item. The user who places the order doesn’t need an immediate response from the warehouse management system, so the request can be sent asynchronously. The asynchronous processing allows the user to continue their work without delays.
Three important considerations to keep in mind, however, are:
- Asynchronous processing isn’t the best approach for every problem. It can be tempting to think of any process that doesn’t directly require user interaction as a good candidate for asynchronous processing, but there are some drawbacks. First, asynchronous processes have no SLA, which means that there’s no guarantee that an asynchronous process will complete within a set amount of time. Second, there’s no guarantee of consistent response latency. If an asynchronous process completes within a certain amount of time, a subsequent process can take a different amount of time to complete. Therefore, even if a user isn’t directly waiting for a response, asynchronous processing might not be right for your scenario if you have subsequent processes that depend on a response, or if you’re concerned that excessive response times might cause your data to become out of sync with the data in an external system.
- There are different ways to process asynchronous requests on the Salesforce Platform, so make sure that you choose the best approach. Tools that support asynchronous processing on the Salesforce Platform work differently “under the hood,” and some were designed for very specific use cases.
- If a tool is asynchronous on the client side, it doesn’t necessarily mean that all of the end-to-end processing is performed in parallel. Depending on the choices that you make, the client’s messages can sometimes still get serialized on the server side.
If you use the wrong tools or the wrong combinations of tools for the wrong jobs, unintended consequences that cancel out the benefits of asynchronous processing can occur. This guide provides explanations and recommendations, as well as potential drawbacks and anti-patterns for various asynchronous use cases, along with the rationale for those recommendations. It also provides insight into how various asynchronous implementation techniques operate and are regulated on the Salesforce Platform.
Note that this guide focuses exclusively on asynchronous processing within the Salesforce Platform. If you are looking for information about asynchronous integration patterns, check out Event-Driven Architecture.
- Takeaway #1: Before using asynchronous processing, make sure that your use cases are valid. Asynchronous patterns have no SLA, are subject to multiple governor mechanisms, can have capacity limits defined based on licensing, and can cause processing delays due to the finite nature of the resources allocated to asynchronous infrastructure within the Salesforce Platform. Take these limitations into serious consideration when using asynchronous processing in scenarios where a user requires a response from the system before they can continue with their work.
- Takeaway #2: Asynchronous processing with Salesforce isn’t a solution for horizontal scaling needs. The Salesforce Platform doesn’t scale infinitely, and asynchronous patterns are still subject to limitations. Asynchronous processing utilizes threads, which contain the contextual information that a CPU needs to execute a stream of instructions. Threads can run in parallel. The number of available threads in any CPU is limited, so the platform has mechanisms in place to ensure that its available threads are utilized as efficiently as possible. The platform’s flow control mechanism prevents orgs from consuming too many threads and negatively affecting other orgs. The platform’s fair usage algorithm controls the number of threads that an org has available for each particular message type.
- Takeaway #3: Know when transactions are truly asynchronous. Some architecture patterns behave asynchronously end-to-end. However, other processes behave asynchronously on the client- or browser-side, but serialize server-side requests, which are then subject to synchronous governor limits.
- Takeaway #4: To build large-scale outbound integrations from the Salesforce Platform, use platform events and robust middleware instead of callouts via asynchronous Apex. Because there are a finite number of asynchronous threads available in the platform, outbound integrations via asynchronous Apex have limited scalability. If your org has outbound integrations that involve high volumes of messages, exceed the number of available threads, and have long processing times, then using asynchronous Apex will inevitably introduce delays.
- Takeaway #5: Make sure to incorporate monitoring when using asynchronous processing. With monitoring, you can detect issues as early as possible and correct them before major performance incidents occur.
- Takeaway #6: Account for events that can cause extreme loads. When you design asynchronous processes, make sure that they can predictably manage workload spikes and lulls. Consider how your implementation handles unexpected events, such as power outages, and design safeguards that mitigate data loss or loss of functionality.
This table outlines the tools that are available for asynchronous processing with Salesforce. Refer to this table to review each tool’s major characteristics during your decision-making process. This guide dives deeper into how to choose from these tools in the next sections.
| Product / Approach | Use Cases | Skills Required | Additional License Required |
|---|---|---|---|
| Apex Future Methods | We recommend that you use Queuable Apex instead of Apex future methods. Queueables have the same use cases as future methods but offer additional benefits. | Pro-code | No. Only certain license types count toward the daily asynchronous Apex execution limit. |
| Queuable Apex | Prefer this approach over future methods. Use for processes that involve long running database operations or external web service callouts. Queueable Apex offers additional benefits over future methods, including job IDs, support for non-primitive types, and job chaining. | Pro-code | No. Only certain license types count toward the daily asynchronous Apex execution limit. |
| Scheduled Apex | Execute Apex at a scheduled time defined by a cron expression. Although the act of scheduling Apex via the cron expression is an asynchronous process, the underlying code executes synchronously when the job starts. | Pro-code | No |
| Apex Continuation Callouts | Execute callouts from Apex methods running in a synchronous transaction context. | Pro-code | No |
| Platform Event Triggers | Loosely couple Salesforce with external systems and communicate between asynchronous components within the Salesforce Platform. | Low-code + Pro-code | Add-on required for high-volume platform event use cases |
| Change Data Capture | Capture and process change events asynchronously after the database transaction is committed. | Pro-code | Add-on license required to scale above baseline allocation. |
| Scheduled Path (After Commit Flows) | Execute at a dynamically scheduled time after a triggering event, such as when a record is created, updated, or deleted. | Low-code | No |
| Asynchronous Path (Record-Triggered Flows) | Execute an operation that you want to run on its own time. Avoid mixed DML errors that occur when you update a value on a related record that isn’t part of the record that triggered a flow. | Low-code | No |
| Outbound Messages | Share data with third party systems in near real-time via the SOAP protocol | Low-code | No |
| Email-to-Case | Automatically create cases and populate case fields based on values in incoming emails. | Low-code | No |
| Batch Apex | Build complex, long-running processes that involve thousands of records by dividing your record set and processing it in manageable chunks. | Pro-code | No. Only certain license types count toward the daily asynchronous Apex execution limit. |
| Scheduled Flows | Execute a flow in the background at a specified time and at a repeated frequency (daily, weekly, or once) to perform actions on a batch of records. | Low-code | No |
| Bulk API | Insert, update, upsert, query, or delete many records asynchronously and process the results later. | Pro-code | No |
| Lightning Actions | Allow Lightning pages to interact with the server without forcing users to fully refresh the page. | Low-code | No |
| Visualforce JavaScript Remoting (Legacy)* | Decouple Visualforce pages from their Apex controllers so that users can perform tasks without having to reload the entire page. | Pro-code | No |
| Salesforce Dashboards | Create visual displays of data from Salesforce reports | Low-code | No |
| CRM Analytics | Explore and visualize CRM data interactively. | Low-code | Yes |
| *Visualforce is no longer the preferred method for building custom UX components in Salesforce, as the Lightning Component framework offers greater flexibility. | |||
This table contains an overview of points to consider as you decide which asynchronous tool to use.
| Skills Required | Some of the tools outlined in this guide require code, while others can be configured declaratively. As you consider your options, think about the skillsets that your team members have. Keep in mind that even if developers are available for your initial implementation (through an implementation partner, for example), you may need to modify your solutions in the future. If your maintenance team lacks developers, a low-code option can be a better fit. |
| Asynchronous with Respect to Client | Determine whether client-side code is executed asynchronously within a browser or mobile app. Remember that even if code executes asynchronously on the client side, there’s a possibility that requests are serialized and executed synchronously on the server side. As you design your solutions, consider how processing is handled on both sides. Make sure that you don’t introduce any unintended bottlenecks. |
| Asynchronous with Respect to Server | Determine whether server-side processing is handled asynchronously within the Salesforce Platform. Keep in mind that asynchronous patterns don’t have SLAs. If you design processes that require a user to receive a response before they can continue with their work, then a tool that processes information synchronously is probably a better fit. |
| Type of Platform Limits Enforced | Determine the type of limits that apply to the execution. As you design your solutions, think carefully about how the volume and frequency of transactions affects the number of SOQL queries that the system needs to execute. Calculate the amount of processing time that each transaction requires and the amount of memory that the system needs to complete the execution. Make sure that the calculated values fall within the limits associated with your selected tools. |
When you select a tool for server-side asynchronous processing, first evaluate your organization’s requirements and available resources. Your goal is to select the tool or tools that minimize implementation and maintenance costs, but still ensure scalability and minimize your likelihood of limit violations. This goal is dependent on the technical considerations outlined in the next sections, as well as the makeup of your team. For example, consider whether you have Apex developers on your team who can maintain your pro-code solution. Otherwise, a declarative approach can make more sense. . Also, keep in mind that different sets of limits can apply to different tools.
Questions to ask:
- How many concurrent processes are needed?
- How will the concurrent process interact with each other?
- How many SOQL queries will execute within each process?
- How many DML operations will execute within each process?
- How long will each process run?
- How sensitive are processes to specific timing?
The Salesforce Platform’s multitenant architecture isolates and concurrently supports the varying requirements of many tenants (orgs). All asynchronous Apex methods covered in this guide are executed within the same asynchronous infrastructure in the Salesforce Platform. They use a message queueing framework that is governed by two primary enforcement mechanisms: flow control and fair usage.
The flow control and fair usage mechanisms prevent a single tenant from using too many server resources and not leaving enough resources for the remaining tenants. Although it’s helpful to understand how these mechanisms work, your key takeaway should be that following asynchronous processing best practices, such as the ones outlined in the next sections, significantly reduces your likelihood of running into issues with them.
The platform’s flow control mechanism prevents one org from flooding a given message type, which consumes too many threads and negatively affects other orgs. Before the framework adds new entries to the queue associated with a message type, it checks the first several thousand existing entries in the queue. If the majority of existing entries are associated with that same sorg and that org already also has entries in worker threads, then the newly added entries are moved to the back of the queue. This process is called re-enqueuing. Re-enqueuing typically happens to Batch Apex and Bulk API processes because they often insert large numbers of entries into their queues at the same time.
The platform’s fair usage mechanism implements a tier-based system. The system ensures that each org on the Salesforce Platform gets a fair share of processing time for a given message type, including Future, Queueable, and Batch message types. If one org dominates a given message type during the same time that other orgs are waiting to perform work on the same message type, the fair usage algorithm reduces the number of threads that the org has available for that particular message type.
See Fair Usage Mechanism.
A benefit of using asynchronous Apex methods is that some governor limits, such as SOQL query limits and heap size limits, are higher. However, don’t rely too heavily on these methods. Due to the finite resources allocated to asynchronous infrastructure, heavy usage of Future, Queueable, and Batch Apex can cause processing delays that stem from limitations based on fair usage and flow control.
Outbound callouts that use asynchronous Apex count against the asynchronous Apex limit. The daily limit is currently 250,000 or 200 times the number of applicable user licenses, whichever is greater. This limit can be an issue for high-volume use cases. If you exceed the limit, your asynchronous Apex jobs and their associated outbound callouts will fail.
Additionally, because the platform has a finite number of asynchronous threads available, outbound integrations via asynchronous Apex have limited scalability. Any high-volume outbound integrations that exceed the number of available threads can have long processing times and lead to delays.
For high-volume use cases, consider these alternative approaches. API calls with these approaches count towards the daily API request limit, which is significantly higher than the daily asynchronous Apex limit. Thus, these approaches are more scalable. Note, however, that there are still physical limitations on the number of concurrent requests that any CPU can process.
- Middleware Scheduled Pull. Avoid delays associated with high-volume outbound integration jobs by using middleware to pull the data on a scheduled basis instead of pushing it via asynchronous Apex. Middleware, such as the MuleSoft Anypoint Platform, can use native SOAP API or REST API in a synchronous fashion so that few, if any, delays are introduced. Middleware can also use the native Bulk API for large data volumes.
See Middleware Scheduled Pull.
- Middleware Pull via Platform Event Notification. Instead of enqueuing Future, Queueable, or Batch asynchronous Apex to perform outbound integrations, you can use platform events. The platform event can either deliver the outbound information itself or instruct a middleware tool to pull the required information via a native API and deliver it to its final destination. Neither of these approaches are subject to the delays that asynchronous Apex is subject to. However, platform event limits still apply.
See Middleware Pull via Platform Event Notification.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Apex Future Methods | We recommend that you use Queuable Apex instead of Apex future methods. Queueables have the same use cases as future methods but offer additional benefits. | Pro-code | Yes | Yes | Asynchronous |
| Queueable Apex | Prefer this approach over future methods. Use for processes that involve long running database operations or external web service callouts. Queueable Apex offers additional benefits over future methods, including job IDs, support for non-primitive types, and job chaining. | Pro-code | Yes | Yes | Asynchronous |
| Scheduled Apex | Execute Apex at a scheduled time defined by a cron expression. Although the act of scheduling Apex via the cron expression is an asynchronous process, the underlying code executes synchronously when the job starts. | Pro-code | Yes | Yes | Synchronous |
| Apex Continuation Callouts | Callouts from Apex methods running in a synchronous transaction context | Pro-code | Yes | Yes | Synchronous |
Queuable Apex With Queuable Apex, you can run Apex processes that involve extensive database operations or external web service callouts asynchronously. To use Queuable Apex, implement the Queueable interface, and then add a job to the Apex job queue. This approach ensures that the asynchronous Apex job runs in the background in its own isolated thread and doesn’t delay the execution of your main Apex logic. Each queued job runs when system resources become available.
Queueable Apex represents the evolution of asynchronous Apex. It offers features that aren’t available to Apex future methods, including:
- Job IDs: When you submit a job by invoking the System.enqueueJob method, the method returns the ID of the new job. You can use this ID to identify your job. To monitor its progress, view the Apex Jobs page in Salesforce Setup, or query the AsyncApexJob object.
- Support for non-primitive types: Queueable classes can contain member variables of non-primitive data types, such as sObjects or custom Apex types.
- Support for job chaining: You can chain Queueable jobs together by starting a second job from a running job. This technique can help you address scenarios involving process dependencies.
- Maximum depth control: You can configure Queueable jobs with a maximum stack depth to ensure that chains of jobs don’t end up with unexpected recursion.
- Deduplication signatures: Queueable jobs provide an optional signature to detect and remove duplicate jobs.
- Configurable delay: You can define a delay of up to 10 minutes before the Queueable job will execute.
- Transaction finalizers: You can attach a post-action sequence to a Queueable job and take relevant actions based on its result. For example, a transaction finalizer can re-enqueue a Queueable job that failed due to an unhandled exception up to five times.
Salesforce uses a queue-based framework to handle asynchronous processes. This queue is used to balance request workloads across orgs. To ensure that your org uses this queue as efficiently as possible:
- Avoid enqueueing future or Queueable methods directly from actions generated by large volumes of end-user activity or integration API calls. This approach can quickly exhaust daily asynchronous Apex limits, resulting in serious business impacts.
- Avoid enqueueing more than one asynchronous action per synchronous action. When multiple asynchronous methods are enqueued from a single synchronous action, the asynchronous activities often execute at the same time and contribute to row lock contention and/or row lock timeout errors.
- Avoid adding large numbers of future or Queueable methods to the asynchronous queue.
- Ensure that future and Queueable methods execute as quickly as possible. The longer a future method takes to execute, the more likely requests behind it in the queue will be delayed. To ensure fast execution of batch jobs, minimize web service callout times, tune queries used in your future methods, and optimize any other object automations including Process Builder processes, workflow rules, flows, and Apex triggers.
- Test your asynchronous methods at scale. Use an environment that generates the maximum number of methods that you expect to handle. This testing helps determine whether you’ll encounter issues with performance or limits in your production environment. Also consider the impact on cumulative daily limits.
- Use Batch Apex instead of future or Queueable methods to process large numbers of records. Batch Apex can handle complex, long-running processes that execute against thousands of records and can be scheduled to run during off hours when more resources are available.
Use scheduled Apex to execute at a specified time and at a repeated frequency as defined by a cron expression. Although the act of scheduling Apex via the cron expression is an asynchronous process, the underlying code executes synchronously when the job starts. Scheduled Apex is ideal for daily or weekly maintenance tasks.
- You can only have 100 scheduled Apex Jobs at one time. To avoid this limitation, consider using a schedule-triggered flow that invokes Apex code instead of using scheduled Apex.
- Use extreme care if you’re planning to schedule a class from a trigger. You must be able to guarantee that the trigger won’t add more scheduled classes than the limit. In particular, consider API bulk updates, import wizards, mass record changes through the user interface, and all cases where more than one record can be updated at a time.
- For one off tasks that need to be scheduled up to 10 minutes in the future, use a delayed Queueable job. This will not count towards the 100 scheduled job limit.
- Scheduled Apex is executed in a synchronous context with synchronous limits.
- Consider how long the scheduled job will execute for. A long running job may overlap with the start of the next scheduled job.
- Salesforce schedules the class for execution at the specified time. Actual execution can be delayed based on service availability. Jobs scheduled to run during service maintenance downtime will be scheduled to run after the service comes back up.
- Use System.scheduleBatch for scheduling batch jobs rather than having a scheduled job with the sole purpose of enqueuing a batch job.
Historically, synchronous callouts made from an Apex method running in a synchronous Apex transaction context, such as a Visualforce controller or a Lightning component controller, were subject to the Apex concurrency limit regarding long-running requests. As of Winter ’19, synchronous callouts are no longer counted as long-running. Although Apex continuations were initially created as a solution to synchronous callouts that resulted in long-running requests, they also provide some additional benefits.
- A single synchronous action can perform up to three continuation actions. The previous continuation must complete prior to a new continuation action being performed.
- Each continuation action can perform up to a maximum of three callouts, which execute in parallel.
- When all of the callouts performed by a continuation action are complete, the platform invokes the continuation callback method to handle the results.
Although continuations execute asynchronously with respect to the originating synchronous action, there are key differences between Apex Continuations and other asynchronous Apex techniques such as future methods, Queueable, or Batch. Key differences are:
- Continuations aren’t queued in any way.
- Continuations don’t contribute to the daily asynchronous Apex execution limit.
- Continuations switch thread context on the application server from a heavyweight Apex platform thread to a lightweight thread that only performs callouts. For the purposes of executing callouts that may run up to 120 seconds, continuations offer significant application server memory savings.
- Originally, continuations could only be executed from a Visualforce JavaScript remoting call. However, in Summer ‘19, continuations were officially added to the Lightning Component framework, which eliminated the need for Visualforce.
Platform events are the Salesforce Platform’s implementation of a purely event-driven architecture. You can find more details about the components associated with this type of architecture in the Architect’s Guide to Event-Driven Architecture.
Platform events and platform event triggers/flows are often great alternatives to running asynchronous Apex. They’re also a great way to invoke off-platform processing. For example, you can use a combination of Amazon Web Services (AWS) event relays and platform events to invoke serverless compute functionality in AWS Lambda. Work that’s being performed relatively quickly and without any callouts (which aren’t possible from a platform event trigger or flow), is a great candidate for a platform event/trigger or event/flow combination.
See Asynchronous Processing via Platform Events.
The events published to the bus via publish after commit are delivered in order and can be bulk processed by the trigger or flow in a separate synchronous context. The context is synchronous and enforces all synchronous governor limits. Choose the platform event trigger/flow batch size carefully to avoid hitting limits.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Platform Event Triggers | Loosely couple Salesforce with external systems and communicate between asynchronous platform components. | Low-code + Pro- code | Yes | Yes | Synchronous |
- As events are published on the event bus, they are available for consumers. In the case of event triggers (Apex or flow), these events are dispatched to available synchronous threads on the app tier (one thread per event trigger/flow). Note that this process doesn’t have an SLA.
- When publish operations are added to the queue, the result of the queueing process is stored in the Database.SaveResult object. These entries only indicate whether or not the queueing operation was successful. To get the final result of a call published through the event bus, implement an Apex publish callback. With this additional information, you can make decisions about further actions, such as attempting to republish failed events.
- While platform events aren’t subject to the same limits as asynchronous Apex, they do have their own sets of governor limits. If you run into issues with limits, you can enable enhanced event usage metrics to determine whether specific events are using up more of your allocations than you intended. If you need greater throughput on event triggers, consider using parallel subscriptions to process events simultaneously instead of in a single stream. With parallel subscriptions, you can scale Apex platform event triggers to handle high volumes of events. Parallel subscriptions are available for custom high-volume platform events but not for standard events or change events.
- Platform events have two options for publish behavior:
- Publish Immediately: Events are published immediately during the transaction, before the final database commit. Events that are published this way aren’t guaranteed to be published in order.
- Publish After Commit: Events are published at the time that the database transaction is successfully committed. Events that are published this way are guaranteed to be published in order.
It’s helpful to use Publish Immediately for use cases such as logging, where you want to publish the logging event regardless of whether the transaction succeeds and commits, or fails and rolls back. It’s possible to immediately publish a platform event that’s consumed by a platform event trigger. The platform event trigger then competes for a database row lock with the transaction that published it. Review all designs thoroughly prior to immediately publishing platform events to ensure that you avoid this scenario.
Asynchronous flows provide low-code alternatives to asynchronous Apex. As with other forms of asynchronous processing, they execute in the background when resources are available. Asynchronous flows also have no SLAs and can have unpredictable wait times.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Scheduled Path (After Commit Flows) | Execute at dynamically scheduled times after triggering events. | Low-code | Yes | Yes | Asynchronous |
| Asynchronous Path (Record-Triggered Flows) | Execute an operation that you want to run on its own time and to avoid mixed DML errors. | Low-code | Yes | Yes | Asynchronous |
Scheduled Paths are cron trigger-based to execute at a specific time. They execute when records are created, updated or deleted and give you granular control over when to run the automation relative to the record change. (Example: send an email to a user one hour before a task is due.) Unlike Apex future methods, which are limited to a maximum of 50 methods enqueued in a synchronous transaction, scheduled flow actions don’t currently have a max enqueue limit per transaction. There is, however, a batch size configuration within the definition of the scheduled path that allows for some control over how many records are handled by the scheduled path flow execution.
Asynchronous Paths can be added to record-triggered flows. They run in the background and don’t delay the execution of the transaction that originally triggered the flow. You can use an asynchronous path to execute a long-running operation or any operation that you want to run on its own time. Asynchronous paths can help avoid mixed DML errors that occur when you need to update a value on a related record that’s not part of the record that triggered the flow.
Note: Outbound messaging is a legacy feature and platform events (described in the previous section) are a more modern approach and offer greater flexibility. Platform Events are Salesforce’s recommended pattern for Event Driven Integration.
Outbound messages provide a means of asynchronous outbound communication via SOAP API. When configured in Salesforce, the outbound message definition produces a SOAP WSDL that can be consumed by an external web service provider. Outbound messages can be triggered from workflow rules, Process Builder processes, or Lightning after-save flow triggers. An outbound SOAP message can contain up to 100 notifications. Each notification contains the object ID and a reference to the associated sObject data. If the information in the underlying object changes after the notification is queued but before it is sent, only the latest data is delivered and not the intermediate changes.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Outbound Messages | Share data with third party systems in near real time via the SOAP protocol | Low-code | N/A | Yes | N/A |
When an outbound message listener (the web service you configured with the generated WSDL) receives a message, it uses the included session ID information to call the Lightning Platform API to update the record in Salesforce that triggered the outbound message.
- Outbound messages aren’t handled via the queue-based asynchronous infrastructure in Salesforce that runs activities such as future methods, Queueable Apex, Batch Apex, Bulk API, and so on. Instead, records are stored locally in the OutBoundMessage object. Messages are dispatched and retried via a local scheduled background process.
- Messages aren’t sent synchronously when the outbound message action is executed by the initiating automation (for example, an after-save flow trigger). Instead, they remain in the OutboundMessage object until sent successfully or until they’re dropped after 24 hours of unsuccessful delivery.
- To avoid an infinite loop of outbound messages, ensure that the user who updates the objects does not have the Send Outbound Messages permission.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Email-to-Case | Automatically create cases and populate case fields based on values in incoming emails. | Low-code | Yes | Yes* | Synchronous |
| * Email-to-Case is handled as both sync and async, but with synchronous Apex limits | |||||
Email-to-Case normally works in a synchronous manner. There’s a limit of four synchronous threads to handle inbound Email-to-Case requests. The synchronous form of the handler maintains a connection to the inbound Salesforce mail server (MTA) that receives the email. However, there are reasons why Email-to-Case can be handled asynchronously:
- Email-to-Case is subject to thread limits, specifically 10 total handler threads per app server: four threads for synchronous processing and six threads for asynchronous processing.
- If a synchronous email handler exceeds 15 seconds of execution time, then that particular email service address is marked as asynchronous for a period of 30 minutes. Subsequent handler requests for that service address result in a message that’s enqueued for MessageTypeName = MAILCATCHER_EMAIL_TO_CASE, along with a reference to the email content.
- If a synchronous thread is already in use for a particular org and service address, then additional requests for that service address are queued asynchronously.
- If a synchronously handled email encounters an error, such as a row lock timeout, then the request is saved and queued asynchronously.
- If no synchronous handler threads are available, then the request is queued asynchronously.
- There is a deduplication option when you configure Email-to-Case, but it doesn’t deduplicate attachments until after the email is processed. This deduplication saves space in the database, but it doesn’t reduce email processing times. You can, however, improve performance by implementing a custom Apex Email-to-Case handler for message processing. This implementation doesn’t affect any governor limits, but it gives the handler access to the email message and all of its attachments before anything is committed to the database. This access allows you to compute checksums for all the included attachments and discard any that you decide are unnecessary, including duplicates, well-known signature pictures, or unwanted file types.
- Committing email attachments to FileForce is responsible for the majority of email processing time. As reply-responses to or from the contact increase and the email chain gets larger, the processing time for each email also grows larger. If the email option “Reply with new content only” isn’t selected, then email chains will grow with each reply. Therefore, we recommend that you use an Apex Email-to-Case handler with logic that implements attachment deduplication at the time of processing.
- Despite the invocation of Apex triggers as part of the handling, Email-to-Case handlers are exempt from the concurrent Apex limit.
To process large volumes of records asynchronously, consider using one of these approaches.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Batch Apex | Complex, long-running processes that involve thousands of records | Pro-code | Yes | Yes | Asynchronous |
| Schedule-Triggered Flows | Perform actions on a batch of records in the background at a specified time and at a repeated frequency via a flow. | Low-code | Yes | Yes | Synchronous |
| Bulk API | Insert, update, upsert, query, or delete many records asynchronously. | Pro-code | Yes | Yes | Asynchronous |
You can use Batch Apex to build complex, long-running processes that involve thousands of records. Batch Apex operates by dividing up your record set and processing it in manageable chunks. As an example, you can build an archiving solution that runs on a nightly basis and adds records older than a certain date to an archive. Or you can build a data cleansing operation that looks at all Accounts and Opportunities on a nightly basis and performs any necessary updates based on a set of predefined criteria.
- Batch Apex is best at processing large amounts of data because asynchronous Apex has higher limits than synchronous Apex. Batch Apex also performs more efficiently when it processes more items per batch because it uses bulk operations that incur less overhead from queue management. Therefore, to avoid triggering the flow control mechanism via queue flooding and avoid exhausting the daily asynchronous Apex limit, don’t create batches with small scope sizes or that have fast processing times.
- Avoid enqueueing batch jobs directly from actions generated by large volumes of end-user activity or integration API calls. This approach can quickly exhaust the flex queue. If you plan to invoke a batch job from a trigger, then you must guarantee that the trigger doesn’t add more batch jobs than the limit.
- Batch Apex is limited to a maximum of five simultaneous threads, which limits its throughput much more than future methods or Queueable Apex.
- Batch jobs involving large volumes of data should ideally be scheduled to run during off peak hours in order to free up resources for processes that require real-time or near real-time responses.
- When you call System.scheduleBatch, the platform schedules your job for execution at the time that you specify. Actual execution occurs on or after that time, depending on service availability.
- The scheduler runs in system context. All classes are executed, regardless of whether or not the user who scheduled the execution has permission to execute the class.
- All scheduled Apex limits apply for batch jobs scheduled using System.scheduleBatch. After the batch job is queued (with a status of Holding or Queued), all batch job limits apply, and the job no longer counts toward scheduled Apex limits.
- For a batch job with a recurring schedule, consider the correct behavior if the previous job is still executing when the new job starts running.
- Batch jobs enqueued from synchronous Apex transactions use the flex queue. The flex queue is limited to a maximum of 100 items at any point. If a transaction attempts to add more entries, then the platform generates errors and doesn’t submit the batch job.
- Callouts and other non-transactional operations from batch jobs must follow idempotent design considerations. Batch jobs queued before a Salesforce service maintenance downtime remain in the queue. After service downtime ends and system resources are available, the queued batch jobs are executed. If a batch job is running when downtime occurs, the batch execution is rolled back and restarted after the service is restored. Because execute methods can therefore run multiple times, any non-transactional operations, such as callouts, can be retried.
- Consider configuring the batch job to raise BatchApexErrorEvent events to handle error and exception scenarios.
A schedule-triggered flow is a flow scheduled to execute at a specified time and at a repeated frequency (daily, weekly, or once) to perform actions on a batch of records. (Example: update a field in all cases with a status of "Open" at 2am every night.) Scheduled Flows are executed via the cron trigger mechanism and are bulk processed.
- A schedule-triggered flow executes 200 records per invocation, but will be executed with multiple concurrent threads if more than 200 records are present.
- Schedule-triggered flows are executed in a synchronous context with synchronous limits.
- There is no way currently to control the number of records processed per scheduled flow invocation. If the execution is for more than 200 records, there is a real danger for Concurrent Apex errors as a result.
Bulk API is based on REST principles and is optimized for working with large sets of data. You can use it to insert, update, upsert, query, or delete many records asynchronously and process the results later. The Salesforce Platform processes the request in the background. In contrast, SOAP API and REST API use synchronous requests and are optimized for real-time client applications that update a few records at a time. You can use both of these APIs for processing many records, but when the data sets contain hundreds of thousands of records, they are less practical. Bulk API’s asynchronous framework is designed to make it simple and efficient to process data from a few thousand to millions of records.
The easiest way to use Bulk API is to enable it for processing records in Data Loader using CSV files. With Data Loader, you don’t have to write your own client app. Sometimes, though, unique requirements necessitate writing a custom app.
There are two Bulk APIs available within the Salesforce Platform.
- Bulk API 2.0 is the newer API. It provides a streamlined and easier-to-use workflow, and is the focus for enhancements.
- The original Bulk API is still fully supported, but is no longer being enhanced. We recommend that you use Bulk API 2.0 where possible.
For more information on API limits, see Bulk API Limits and Bulk API 2.0 Limits.
Although Salesforce handles the majority of asynchronous requests on the server side, tools that support asynchronous processing on the client side are also available. Client-side asynchronous processing is subject to the limitations of the browser or the mobile application within which it is executing.
Questions to Ask:
- Will your client-side code invoke Apex in any way? If so, Apex synchronous governor limits apply.
- Have you ensured that the payloads you send to the server are as small as possible?
- Does your application use polling or timers? Make sure to set your polling intervals correctly and use $A.getCallback() to wrap code that modifies components outside of the normal rerendering cycle.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Lightning Actions | Allow Lightning pages to interact with the server without full page refreshes | Low-code | Yes | No | Synchronous |
| Visualforce JavaScript Remoting (legacy)* | Allow Visualforce pages to interact with the server without full page refreshes | Pro-code | Yes | No | Synchronous |
| *Visualforce is no longer the preferred method for building custom UX components in Salesforce, as the Lightning Framework offers greater flexibility | |||||
All Lightning communications from the browser to the Salesforce Platform take the form of XMLHttpRequest (XHR) requests. This allows Lightning pages to interact with the server without having to fully refresh the page. Each of these actions results in a synchronous transaction context on the Salesforce Platform.
- Lightning actions, despite being asynchronous with respect to the browser, are executed on the Salesforce Platform under a synchronous transactional context and are subject to synchronous limits. Standard Lightning actions may or may not invoke Apex on the Salesforce Platform. If Apex is invoked in any way during the Lightning action, the Apex synchronous governor limits will all apply during the transaction.
- When making requests to the server, it is best to send and receive as little data as possible in the payload. Every action by a user could potentially spawn multiple requests, and it is important that these be processed efficiently. Sending more data than necessary can contribute to slow transport and increased processing time on the client or server.
- When multiple components are listening for an application event, developers should ensure the event handler will only execute when it is actually desired and useful. For example, in the Lightning Console, components contained in tabs that are not focused can still be listening even though they are not visible. A developer can use various techniques such as using a background utility item as the only listener, or calling getEnclosingTabId() to determine whether this instance of the component is enclosed within the focused tab to ensure each event is handled only when it is intended.
Lightning applications involve frequent communication to the Salesforce Platform to support the Lightning Framework. To support this, multiple requests can be issued at the same time, or “boxcar’ed” in a single XHR request to the Salesforce core platform. The maximum boxcar limit for Lightning is 2500 maximum actions per request. However, each of the boxcars within a request will be processed serially and in order in a separate transaction context. For example, if 100 custom Apex Lightning actions are boxcar’ed together and each takes four seconds to run, 100 separate Apex transactions will be executed on the Salesforce Platform under the same request, taking a total of 400 seconds to complete. In the browser, there would be a single XHR request issued that would take 400 seconds before a response was received.
See Boxcar’ing in Lightning Actions.
If you are building an application that uses polling or timers, you should wrap any code that modifies a component outside the normal rerendering lifecycle in $A.getCallback(). This will ensure that the framework will rerender the modified component and processes any enqueued actions right away. Not taking this approach can cause actions to be queued up indefinitely until a callback is requested through a different process (for example, a user returns to their desk and performs some other action in the system), which will send all of the queued up actions to the server simultaneously in transactions that have very high numbers of boxcars.
Note: Visualforce is no longer the preferred method for building custom UX components in Salesforce, as the Lightning Framework (described in the previous section) offers greater flexibility. However, you may still encounter Visualforce pages in existing implementations.
JavaScript remoting is a tool that front-end developers can use to make an Ajax request from a Visualforce page directly to an Apex controller. JavaScript remoting allows you to run asynchronous actions by decoupling the page from the controller and performing tasks on the page without having to reload the entire page. This helps alleviate view state issues while still executing in the context of the user viewing the page. JavaScript remoting also allows you to ensure that you’re passing only the data that you need each time that you make a call.
- While JavaScript remoting is asynchronous with respect to the Visualforce page, there are limitations to be aware of when using it. First, although asynchronous to the page, all JavaScript remoting requests are enforced by synchronous Apex governor limits on the Salesforce Platform, most importantly:
- Synchronous Apex CPU Limit - default enforced at 15 seconds
- Synchronous SOQL Limit - default 100 SOQL per transaction
- Synchronous DML Limit - same for both synchronous and asynchronous actions (150 statements, 10,000 rows)
- Synchronous Apex heap limit - 6MB
- JavaScript remote requests are limited to a maximum of 120 seconds before being terminated
- In addition to the core platform governor limits, it is also important to note that most browsers only support six simultaneous requests. This means that once six Visualforce remote requests are in flight, additional requests will become queued within the browser and will not execute until one or more of the in-flight requests have been completed.
- It is not uncommon for our support teams to report seeing excessive polling of Salesforce from Visualforce pages that are performing activities such as checking case queue status or refreshing page data. This is usually a sign that the polling interval in the apex
component is set too low. Low polling intervals coupled with large numbers of end users can result in DB throttling due to excessive numbers of simultaneous threads holding database connections. - Failure to clear timer intervals in JavaScript can cause them to accumulate, which will result in large numbers of JavaScript remoting requests executing even for a single user. As with excessive polling, this can result in database throttling due to connection pool exhaustion.
Multiple JavaScript remoting requests can be issued at the same time by boxcar’ing them in a single HTTP request to the Salesforce core platform. On the platform, each of the boxcars (i.e. a separate JavaScript remoting action) will be processed serially and in order in a separate Apex synchronous transaction context. For example, if 10 remoting actions were boxcar’ed together and each took four seconds to run, 10 separate Apex transactions would be executed on the Salesforce Platform under the same request, taking a total of 40 seconds to complete. There would be a single request issued in the browser that would take 40 seconds before a response was received.
See Boxcar’ing in JavaScript Remoting.
| Product / Approach | Use Cases | Skills Required | Asynchronous with Respect to Client | Asynchronous with Respect to Server | Type of Platform Limits Enforced |
|---|---|---|---|---|---|
| Salesforce Dashboards | Create visual displays of data from Salesforce reports. | Low-code | Yes | Yes | Asynchronous* |
| CRM Analytics | Explore and visualize CRM data interactively. | Low-code | Yes | Yes | Asynchronous |
| *Dashboards execute asynchronously and are subject to the same fair usage and flow control limits as asynchronous Apex message types. | |||||
Unlike reports that run directly via the UI or API, dashboard-based reports execute asynchronously and are subject to both fair usage and flow control limitations. Dashboard-executed reports map into four separate asynchronous message queues for the reports: SMALL, MEDIUM, LARGE, and UNKNOWN. When a dashboard is refreshed, the reports underlying the dashboard panels are submitted to the various dashboard message queues. As the reports complete execution, the various dashboard panels render in the UI.
Reports used for dashboards must follow all report performance best practices. If any of the reports that a dashboard executes time out during execution, the dashboard status remains stale. When a user lands on a stale dashboard, the reports are automatically resubmitted to the message queues to execute. This can lead to message queue thread starvation of other message types due to overwhelming volumes of dashboard report message types.
There are four scenarios where asynchronous processing occurs within CRM Analytics:
- Dataflows make data from Salesforce and other systems available to dashboards in CRM Analytics.
- Remote Analytics allow you to combine data from multiple Salesforce orgs into a single CRM Analytics dashboard.
- Mass Updates allow you to update Salesforce records en masse directly from a CRM Analytics Dashboard.
- Writebacks allow you to load data from external systems into dashboards and then copy it to Salesforce.
All of the processes mentioned above utilize Bulk API in the background, but instead of being subject to Bulk API’s limits, they are subject to CRM Analytics Limits. Keep these limits in mind when working with large data volumes in CRMA.
Because asynchronous processing happens in the background, any failures that occur typically aren’t reported in the UI. Additionally, while rare, the platform’s flow control mechanism or fair usage algorithm can determine that asynchronous jobs must be put on hold to ensure platform stability. This hold can create scenarios where synchronous processes attempt to perform CRUD operations on records that should already have been updated by asynchronous processes that haven’t run yet.
These issues make monitoring and alerting for asynchronous processes critical. Monitoring and alerting enables you to detect issues quickly and either fix them on your own or reach out to Salesforce for assistance. They represent a cost-effective failure mitigation or risk management strategy so your asynchronous processes remain highly available.
You may have seen guidance on third-party sites for designing custom monitoring and alerting solutions. Their approaches often require you to generate error logs when Apex code is executed, and then insert the logs into a custom object or a big object. We don’t recommend this approach for these reasons.
- This type of error logging only works when code is executed, not when code is NOT executed, which limits what you can actually monitor. For example, if an unexpected failure causes process execution to stop before the error logging is completed, then the log entry doesn’t get created and teams relying on the log for monitoring will not know what happened.
- Error logs stored in custom objects can consume data storage quickly and lead to large data volume issues.
- The act of writing logs into records consumes database connections and CPU processing time, which isn’t ideal in high-volume usage scenarios where server resource utilization should be kept to a minimum.
Take some time to explore all available monitoring and debugging options on the platform before opting to use custom error logging as your primary monitoring and alerting solution. Instead, consider these two alternative approaches.
If you want to build your own monitoring and alerting solution, you can write SOQL queries against the AsyncApexJob object to check the status of:
- Any Apex batch job.
- Any method with the future annotation.
- Any job that implements the Queueable or Schedulable interface.
With this approach, you can design a UI component that allows users to execute a query and view the results on demand, or you can build a background job that executes the query periodically and sends alerts if it detects any anomalies.
See Asynchronous Apex Monitoring via Custom Query.
Here are some considerations to keep in mind with this approach.
- Although you can build a scheduled Apex job that repeatedly runs a query to monitor your asynchronous processes, the job itself will run asynchronously and will be subject to the same issues as other asynchronous processes. A better solution is to design an external process that calls an API in Salesforce to run the query. With this approach, the query will run synchronously instead of asynchronously.
- Make sure to include a specific time frame and set of statuses and types of AsyncApexJob in your query. This will limit the number of records that get returned and improve performance. Also, make sure to test your query’s performance in a full sandbox prior to moving it into production.
- Avoid executing your query more than once every five minutes, as it will consume database processing resources and can interfere with other processes if it runs too frequently.
Proactive Monitoring allows you to receive performance and dequeue latency alerts that get triggered during the execution of asynchronous processes. System administrators can elect to receive notifications via email, review past alert trends in a self-service portal on Salesforce Help, and in specific critical scenarios, alerts can also automatically create Salesforce support cases, which will greatly reduce our support team’s time to detect (TTD) and time to engage (TTE). This feature requires a Signature Success Plan.
Refer to this guide when building or considering asynchronous processing on the Salesforce Platform. It’s always a good idea to understand the full scope of options available to you, and how they may fit with your specific use case. Be sure to thoroughly assess your current landscape before making changes to any of your existing architectures, especially if your current solution is working well.
Help us make sure that we’re publishing what is most relevant to you. Take our survey to provide feedback on this content, and tell us what you want to see next.