Clear Dynamics 365 System Jobs for Status Reasons – Waiting for Resources, Succeeded and Cancelled

Administrators need to be careful while configuring workflows and their trigger conditions in Dynamics CRM. Sometimes when we call workflows by using the wrong condition, like update of any field, and the workflow calls other child workflows then this could cause a loop. Dynamics 365 detects infinite loop but if you have many such workflows chained together then by the team Dynamics 365 detects the loop it is already too late. In Dynamics 365 online this could result in a huge problem as these workflows can consume all your storage space and they can render your system unusable.

Whenever a workflow is called, it creates a record in the system job table in the backend. And when workflows are called in a loop it creates a huge bunch of records in the system job table and consumes your whole space. One of our clients faced a very similar issue.

system job table

We have followed following steps to clear space consumed by system job.

1)

First, we deactivated the workflows which were being called due to wrong business logic.

deactivated the workflows

2)

“Waiting For Resources” system job records can’t be deleted as it is. First, we need to cancel these system jobs.

So, we have written a utility to achieve this. First, we will fetch those system job records which are “Waiting For Resources” and then update their status to “Cancelled”.

system job records

In the following code we have cancelled all “System Jobs” records which are “Waiting For Resources”.

public void UpdateInBulk (IOrganizationService _orgService)
        {
            Int32 pageNumber = 1;
            // Initialize the number of records.
            int fetchCount = 1000;
            // Specify the current paging cookie. For retrieving the first page,
            // pagingCookie should be null.
            string pagingCookie = null;
            string fetchXml = @"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                  <entity name='asyncoperation'>
                                    <attribute name='asyncoperationid' />
                                    <attribute name='name' />
                                    <attribute name='regardingobjectid' />
                                    <attribute name='operationtype' />
                                    <attribute name='statuscode' />
                                    <attribute name='ownerid' />
                                    <attribute name='startedon' />
                                    <attribute name='statecode' />
                                    <order attribute='startedon' descending='true' />
                                    <filter type='and'>
                                      <condition attribute='operationtype' operator='eq' value='10' />
                                      <condition attribute='statuscode' operator='eq' value='0' />
                                    </filter>
                                  </entity>
                                </fetch>";
            while (true)
            {
                // Build fetchXml string with the placeholders.
                string xml = CreateXml (fetchXml, pagingCookie, pageNumber, fetchCount);
                // Excute the fetch query and get the xml result.
                RetrieveMultipleRequest fetchRequest1 = new RetrieveMultipleRequest
                {
                    Query = new FetchExpression(xml)
                };

                EntityCollection returnCollection = ((RetrieveMultipleResponse)_orgService.Execute(fetchRequest1)). EntityCollection;
                List<Entity> updateCollection = new List<Entity>();                   

                foreach (var c in returnCollection.Entities)
                {                           

                    var updateEntity = new Entity("asyncoperation");
                    updateEntity.Id = c.Id;
                    updateEntity["statecode"] = new OptionSetValue(3);  // 3 is for status cancelled                      

                    try
                    {
                        updateCollection.Add(updateEntity);
                    }
                    catch (Exception ex)
                    {

                    }
                }
                BulkUpdateSystemJobRecords (_orgService, updateCollection);
                // Check for morerecords, if it returns 1.
                if (returnCollection.MoreRecords)
                {
                    Console.WriteLine("\n****************\nPage number {0}\n****************", pageNumber);
                    // Increment the page number to retrieve the next page.
                    pageNumber++;
                    // Set the paging cookie to the paging cookie returned from current results.
                    pagingCookie = returnCollection.PagingCookie;
                }
                else
                {
                    // If no more records in the result nodes, exit the loop.
                    break;
                }
            }
        }
        public static void BulkUpdateSystemJobRecords(IOrganizationService service, List<Entity> entities)
        {
            // Create an ExecuteMultipleRequest object.
            var 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()
            };

            // Add a UpdateRequest for each entity to the request collection.
            foreach (var entity in entities)
            {
                UpdateRequest updateRequest = new UpdateRequest {Target = entity};
                multipleRequest.Requests.Add(updateRequest);
            }

            // Execute all the requests in the request collection using a single web method call.
           try
            {
                ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);
            }
            catch (Exception ex)
            {
            }

        }
        public string ExtractNodeValue(XmlNode parentNode, string name)
        {
            XmlNode childNode = parentNode.SelectSingleNode(name);

            if (null == childNode)
           {
                return null;
            }
            return childNode.InnerText;
        }

        public string ExtractAttribute(XmlDocument doc, string name)
        {
            XmlAttributeCollection attrs = doc.DocumentElement.Attributes;
            XmlAttribute attr = (XmlAttribute)attrs.GetNamedItem(name);
            if (null == attr)
            {
                return null;
            }
            return attr.Value;
        }

        public string CreateXml(string xml, string cookie, int page, int count)
        {
            StringReader stringReader = new StringReader(xml);
            XmlTextReader reader = new XmlTextReader(stringReader);

            // Load document
            XmlDocument doc = new XmlDocument();
            doc.Load(reader);

            return CreateXml(doc, cookie, page, count);
        }

        public string CreateXml(XmlDocument doc, string cookie, int page, int count)
        {
            XmlAttributeCollection attrs = doc.DocumentElement.Attributes;

            if (cookie != null)
            {
                XmlAttribute pagingAttr = doc.CreateAttribute("paging-cookie");
                pagingAttr.Value = cookie;
                attrs.Append(pagingAttr);
            }

            XmlAttribute pageAttr = doc.CreateAttribute("page");
            pageAttr.Value = System.Convert.ToString(page);
            attrs.Append(pageAttr);

            XmlAttribute countAttr = doc.CreateAttribute("count");
            countAttr.Value = System.Convert.ToString(count);
            attrs.Append(countAttr);

            StringBuilder sb = new StringBuilder(1024);
            StringWriter stringWriter = new StringWriter(sb);

            XmlTextWriter writer = new XmlTextWriter(stringWriter);
            doc.WriteTo(writer);
            writer.Close();

            return sb.ToString();
        }

 

3)

Now we can run bulk deletion job. Go to “System Jobs” from Settings.  Go to More Actions and click on Bulk Delete.

system jobs bulk delete

define search criteria

select values

4)

When “Bulk Delete” job is run successfully space is available automatically. However, we will have to wait for a few hours till “Total Storage Used” status is updated in your Dynamics 365. You can check the status in “Service Health” section of your Dynamics CRM.

Dynamics 365 admin center sevice health

That’s it for today. Hope this will be helpful next time you face such scenarios. Thanks!!!

 

Leave a Reply

Your email address will not be published. Required fields are marked *