Avoid usage of batch request types in plug-ins and workflow activities

Applies to:

  • Dynamics 365 (online), version 9.0
  • Microsoft Dynamics 365
  • Dynamics CRM 2016 Online and Dynamics CRM 2016 on-premises
  • Dynamics CRM 2015
  • Dynamics CRM 2013
  • Dynamics CRM 2011

Category: Usage, Reliability, Performance

Impact potential: Medium

Symptoms

The following are possible effects when using ExecuteMultipleRequest or ExecuteTransactionRequest message request classes within the context of a plug-in or workflow activity:

  • Due to their long-running nature, batch request messages expose sandbox-isolated plug-in types to the two-minute (2000-ms) channel timeout exception and can degrade the user experience for synchronous registrations.

  • Batch requests are subject to concurrency throttling, which can cause unnecessary server busy exceptions when the plug-in is executed by multiple threads. There is a limit of two concurrent ExecuteMultiple operations per online instance.

    Note

    For on-premises deployments, the ExecuteAsyncPerOrMaxConnectionsPerServer setting enables the same throttling. By default, it is not defined for on-premises.

Guidance

Use these batch messages where code is being executed outside of the platform execution pipeline, such as integration scenarios where network latency would likely reduce the throughput and increase the duration of larger, bulk operations.

More specifically, use them in the following scenarios:

  • Use ExecuteMultipleRequest to bulk load data or external processes that are intentional about executing long-running operations (greater than two minutes).

  • Use ExecuteMultipleRequest to minimize the round trips between custom client and Dynamics 365 servers, thereby reducing the cumulative latency incurred.

  • Use ExecuteTransactionRequest for external clients that require the batch of operations to be committed as a single, atomic database transaction or rollback if any exception is encountered. Be aware of the potential for database blocking for the duration of the long-running transaction.

Problematic patterns

Below is an example usage of ExecuteMultipleRequest in the context of a plug-in.

Warning

This scenario should be avoided.

public class ExecuteMultipleRequestInPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        IOrganizationService service = factory.CreateOrganizationService(context.UserId);

        QueryExpression query = new QueryExpression("account")
        {
            ColumnSet = new ColumnSet("accountname", "createdon"),
        };

        //Obtain the results of previous QueryExpression
        EntityCollection results = service.RetrieveMultiple(query);

        if (results != null && results.Entities != null && results.Entities.Count > 0)
        {
            ExecuteMultipleRequest batch = new ExecuteMultipleRequest();
            foreach (Entity account in results.Entities)
            {
                account.Attributes["accountname"] += "_UPDATED";

                UpdateRequest updateRequest = new UpdateRequest();
                updateRequest.Target = account;

                batch.Requests.Add(updateRequest);
            }

            service.Execute(batch);
        }
        else return;

    }
}

This example includes usage of the type directly with the Execute method. The usage can be anywhere within the context of a plug-in or workflow activity execution. This might be in a method that is contained within the same or a separate class, as well. It isn't limited to being directly contained in the Execute method definition.

Additional information

ExecuteMultiple and ExecuteTransaction messages are considered batch request messages. Their purpose is to minimize round trips between client and server over high-latency connections. Plug-ins either execute directly within the application process or in close proximity when sandbox-isolated, meaning latency is rarely an issue. Plug-in code should be very focused operations that execute quickly and minimize blocking to avoid exceeding timeout thresholds and ensure a responsive system for synchronous scenarios.

On the server side, the operations included in a batch request are executed sequentially and aren't done in parallel. This is the case even if the ExecuteMultipleSettings.ReturnResponses property is set to false. Developers tend to use batch requests in this manner assuming that it will allow for parallel processing. Batch requests won't accomplish this objective. Another common motivator is an attempt to ensure that each operation is included in a transaction. This is unecessary because the plug-in is often already being executed within the context of a database transaction, negating the need to use the ExecuteTransaction message.

See also

Event execution pipeline
Run-time limitations
Use ExecuteMultiple to improve performance for bulk data load
Execute messages in a single database transaction