Introduction
First, let’s set the scene. We have built a solution for a customer with a site collection and within that site collection a sub-web which has a large number of document libraries in it.
For the next release we were looking to setup records management and use the out of the box disposition workflow to allow the customer to decide whether to delete the document or not. The workflow is triggered by an information policy.
The upgrade went well all the feature upgrades worked nicely however then we hit a snag setting up the workflows.
The process of applying a workflow to a content type is pretty straight forward and we did not have any problems with the development, staging or UAT environments. Of course its only when we go to setup in production did we hit a problem.
Applying the workflow to a content type was achieved by doing the following:-
- Browse to the root of your site collection
- Click on Site Content –> Settings
- Click on Site Content Types
- Click on your content type that you wish to configure
- Click “Workflow Settings”
- Click Add a Workflow which takes you into the following screen
If you look at the image at the bottom of the page you will see the option “Update all content types”, so we set this and clicked Ok.
The page just hung there processing, we left it for a while but still it was hung there. Knowing SharePoint we thought that is fine we’ll leave it. Two hours later still nothing so we started to check the SharePoint Unified Logs and unfortunately we found an error
SharePoint Foundation Database fa46 High at Microsoft.SharePoint.SPSqlClient.ExecuteQueryInternal(Boolean retryfordeadlock) at Microsoft.SharePoint.SPSqlClient.ExecuteQuery(Boolean retryfordeadlock) at Microsoft.SharePoint.Library.SPRequestInternalClass.GetListsWithCallback(String bstrUrl, Guid foreignWebId, String bstrListInternalName, Int32 dwBaseType, Int32 dwBaseTypeAlt, Int32 dwServerTemplate, UInt32 dwGetListFlags, UInt32 dwListFilterFlags, Boolean bPrefetchMetaData, Boolean bSecurityTrimmed, Boolean bGetSecurityData, Boolean bPrefetchRelatedFields, ISP2DSafeArrayWriter p2DWriter, Int32& plRecycleBinCount) at Microsoft.SharePoint.Library.SPRequestInternalClass.GetListsWithCallback(String bstrUrl, Guid foreignWebId, String bstrListInternalName, Int32 dwBaseType, Int32 dwBaseTypeAlt, Int32 dwServerTemplate, UInt32 dwGetListFlags, UInt32 dwListFilterFlags, Boolean bPrefetchMetaData, Boolean bSecurityTrimmed, Boolean bGetSecurityData, Boolean bPrefetchRelatedFields, ISP2DSafeArrayWriter p2DWriter, Int32& plRecycleBinCount) at Microsoft.SharePoint.Library.SPRequest.GetListsWithCallback(String bstrUrl, Guid foreignWebId, String bstrListInternalName, Int32 dwBaseType, Int32 dwBaseTypeAlt, Int32 dwServerTemplate, UInt32 dwGetListFlags, UInt32 dwListFilterFlags, Boolean bPrefetchMetaData, Boolean bSecurityTrimmed, Boolean bGetSecurityData, Boolean bPrefetchRelatedFields, ISP2DSafeArrayWriter p2DWriter, Int32& plRecycleBinCount) at Microsoft.SharePoint.SPListCollection.EnsureListsData(Guid webId, String strListName) at Microsoft.SharePoint.SPListCollection.GetListByName(String strListName, Boolean bThrowException) at Microsoft.SharePoint.Workflow.SPWorkflowAssociation.EnsureUtilityList(SPWeb web, String workflowName, String listTitle, Guid Id, SPListTemplateType templateType, Boolean forceListCreation, String alternateNameRes, String descriptionRes) at Microsoft.SharePoint.Workflow.SPWorkflowAssociation.EnsureTaskList(SPWeb web, String workflowName, String createTaskListTitle, Guid createTaskListGuid, Boolean forceListCreation) at Microsoft.SharePoint.Workflow.SPWorkflowAssociationCollection.SetUtilityLists(SPWorkflowAssociation wa, Boolean forceUtilityListCreation) at Microsoft.SharePoint.Workflow.SPWorkflowAssociationCollection.AddCore(SPWorkflowAssociation wa, Guid id, SPList list, Boolean forceUtilityListCreation) at Microsoft.SharePoint.Workflow.SPContentTypeWorkflowAssociationCollection.AddCoreCT(SPWorkflowAssociation wa) at Microsoft.SharePoint.Workflow.SPContentTypeWorkflowAssociationCollection.PushDownAssociation(SPWorkflowAssociation associationTemplate, Boolean bUpdateIfExisting, MethodBase mbChangeEntry) at Microsoft.SharePoint.Workflow.SPContentTypeWorkflowAssociationCollection.UpdateOrAdd(SPWorkflowAssociation associationTemplate) at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) at Microsoft.SharePoint.SPChangeMonitor.ApplyChangesCore(Object ct, Boolean applyAll, Type typeFilter, Boolean bFilterInclude) at Microsoft.SharePoint.SPContentType.PushDownWorkflowChangesToDerivedCTCore(SPContentType ct, Boolean fCloseWebAsNecessary) at Microsoft.SharePoint.SPContentType.PushDownWorkflowChangesToListCTs(SPContentTypeCollection cts, IEnumerable`1 cids, Boolean throwOnSealedOrReadOnly) at Microsoft.SharePoint.SPContentType.PushDownChanges(CodeToPushDownChangesToDerivedCT derivedCTPushdownCode, CodeToPushDownChangesToListCTs listDerivedCTsPushdownCode, Boolean throwOnSealedOrReadOnly, IList`1 exceptions) at Microsoft.SharePoint.SPContentType.UpdateWorkflowAssociationsOnChildren(Boolean bGenerateFullChangeList, Boolean bPushdownDerivedCT, Boolean bPushdownListCTs, Boolean bThrowOnSealedOrReadOnly) at Microsoft.SharePoint.WorkflowServices.ApplicationPages.WrkSetngPage.OnClick_Update(Object sender, EventArgs e) at System.Web.UI.WebControls.LinkButton.RaisePostBackEvent(String eventArgument) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest() at System.Web.UI.Page.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error) at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb) at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus) at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
The reason for this error was explained in the next log entry which was a little more concise:-
Getting Error Message for Exception System.Web.HttpException (0x80004005): Request timed out
So a bit of head scratching and we thought well we should really make this change via PowerShell but the problem is how to do that?
Solution
Well its funny some of the method calls you see on SharePoint objects. Obviously the guys at Microsoft have seen this issue before. When I was updating the SPContentType I remember seeing the function UpdateWorkflowAssociation() but then there was the much more useful UpdateWorkflowAssociationsOnChildren()!
Thanks to the team at Microsoft – that is just what we were looking for!
The fix was the following piece of PowerShell:-
$web = Get-SPWeb [your site collection root web url]; $contentTypeToUpdate = $web.ContentTypes | ?{$_.Name –eq “[Your Content Name Here]”}; $contentTypeToUpdate.UpdateWorkflowAssociationsOnChildren($true, $true, $true, $false);
I used ULSViewer to watch the function running SPSqlClient sessions to see that the process was working correctly and more importantly it wasn’t timing out!
To talk through the PowerShell we are doing the following:-
- Getting the SharePoint web for the root of our site collection which contains the content type to update
- Get the content type that we need to update
- Call the UpdateWorkflowAssociationsOnChildrenUpdate()
- mark all the content as changed = true
- push down to all derived content types = true
- push down to all content types that are associated to lists = true
- throw an error if you encounter a read-only or sealed content type = false
Lessons Learned
When you are doing upgrades always try and use a similar sized data set as production!
When it does not work through the UI – bring on the PowerShell!
Anyway I hope that helps someone else get themselves out of trouble!
Reblogged this on Sutoprise Avenue, A SutoCom Source.
Reblogged this on Mai Omar Desouki – Avid SharePointer.