Asked by:
Plugin get timed out

Question
-
Hello,
I'm writing a plugin with requirement as follow: When a specific field is updated on a parent account, then it should update that specific field on all of the child accounts as well
The plugin successfully works and update all of the child accounts correctly.
However my problem is for a parent account that has a lot of child accounts (over 1000), this plugin keeps getting timed out.
We register the plugin in sandbox mode so it gets timed out after 2 minutes.
We register the plugin on Asynchronous mode because otherwise the users will have to 'wait' for a long time until the plugin is finished executing. It's for Post-operation stage. I'm using Post Image in this plugin.
Here's my code.
I suspect that the problem comes inside the FOR loop when doing service.Update(property) for each of the child account.
I'm wondering if there's a more efficient way to update all of these child accounts?
Any suggestion is greatly appreciated. Thanks much,
tri
public override void OnExecute(IServiceProvider serviceProvider, IPluginExecutionContext context) { var trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); // The InputParameters collection contains all the data passed in the message request. //Make sure were not in an infinite loop if (context.Depth > 1) { return; } var targetEntity = context.GetParameterCollection<Entity>(context.InputParameters, "Target"); if (targetEntity == null) throw new InvalidPluginExecutionException(OperationStatus.Failed, "Target Entity cannot be null"); var postImage = context.PostEntityImages["PostImage"]; if (postImage == null) throw new InvalidPluginExecutionException(OperationStatus.Failed, "Post Image is required"); var portfolio = context.GenerateCompositeEntity(targetEntity, postImage); var portCustSatisfaction = (OptionSetValue) portfolio.Attributes[Schema.Account.CustSatisfaction]; Guid portfolioId = (Guid) portfolio.Attributes["accountid"]; var serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof (IOrganizationServiceFactory)); var service = serviceFactory.CreateOrganizationService(context.UserId); //here run a FetchXML to grab all child accounts var childProperties = service.RetrieveMultiple(new FetchExpression(string.Format(RetrieveChildProperties, portfolioId))); if (childProperties != null && childProperties.Entities.Count >= 1) { foreach (Entity property in childProperties.Entities) { property[Schema.Account.CustSatisfaction] = new OptionSetValue(portCustSatisfaction.Value); service.Update(property); } } }
Wednesday, December 16, 2015 9:08 PM
All replies
-
Ok I just read an MSDN article about ExecuteMultipleRequest and giving it a try now.
I modified my code as below however now I get a different error message "ExecuteMultiple Request batch size exceeds the maximum batch size allowed".
Turned out there is a limit of 1000 per batch and this account has just over 1000 sub accounts... Is there a way to increase this limit?
Appreciate your suggestions, thanks.
-tri
var postImage = context.PostEntityImages["PostImage"]; if (postImage == null) throw new InvalidPluginExecutionException(OperationStatus.Failed, "Post Image is required"); var portfolio = context.GenerateCompositeEntity(targetEntity, postImage); // Create an ExecuteMultipleRequest object. ExecuteMultipleRequest multipleRequest = new ExecuteMultipleRequest() { // Assign settings that define execution behavior: continue on error, return responses. Settings = new ExecuteMultipleSettings() { ContinueOnError = false, ReturnResponses = true }, // Create an empty organization request collection. Requests = new OrganizationRequestCollection() }; var portCustSatisfaction = (OptionSetValue) portfolio.Attributes[Schema.Account.CustSatisfaction]; //throw new InvalidPluginExecutionException(OperationStatus.Failed, "Customer Satisfaction: "+portCustSatisfaction.Value); Guid portfolioId = (Guid) portfolio.Attributes["accountid"]; var serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof (IOrganizationServiceFactory)); var service = serviceFactory.CreateOrganizationService(context.UserId); var childProperties = service.RetrieveMultiple(new FetchExpression(string.Format(RetrieveChildProperties, portfolioId))); if (childProperties != null && childProperties.Entities.Count >= 1) { foreach (Entity property in childProperties.Entities) { property[Schema.Account.CustSatisfaction] = new OptionSetValue(portCustSatisfaction.Value);
UpdateRequest updateRequest = new UpdateRequest(); updateRequest.Target = property;multipleRequest.Requests.Add(updateRequest); } service.Execute(multipleRequest); }
- Edited by triangular Wednesday, December 16, 2015 11:30 PM
Wednesday, December 16, 2015 11:23 PM -
https://msdn.microsoft.com/en-us/library/gg309717.aspx
Use pagging cookie and fetchxml to get the data in batches and update themRegards Faisal
Thursday, December 17, 2015 11:43 AM -
Hi,
You have to update the records in 1000 records baches. You will execute 1000 records in every batch.
Or
You can convert this into workflow.
Thanks!
Kalim
Thursday, December 17, 2015 2:23 PM -
fetchCount is the bath size. keep it to 500. Plugin and workflow assembly are almost same thing therefore both will work.
Regards Faisal
Friday, December 18, 2015 10:17 AM -
There are two ways you can tackle the problem:
Method 1: use your existing approach and change following
- Use ExecutleMultiple
- Use batching. Ideal batch size is 100-500. Maximum allowed is 1000
Method 2: Create an async workflow and call it on demand from your plugin when there are many records to process.
Tuesday, December 22, 2015 7:56 PM -
Thanks for the suggestions but I'm still stumped... changed to use custom workflow assembly but still getting timed out for over 1000 records...
Any suggestion is appreciated. Thanks,
-tri
Wednesday, December 23, 2015 8:26 PM -
Are you waiting for workflow to finish execution in your plugin?
Monday, December 28, 2015 9:47 PM