I was struggle for a little bit during the WSUS clean up process. For me it timed-out every time.... I knew there were lots of updates that needed to be removed from the server.
Here is the PowerShell command I was running to do the clean-up:
Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteUpdates -CleanupUnneed
For me the cause of the problem very slow SQL commands running in the WSUS database... which was the result of missing indexes. (By the way --- our WSUS server is running Windows Server 2016 with the latest patches, and the Database
Engine is SQL Server 2016).
Before continuing.... please note that I have made changes to our WSUS database to increase performance. No one else approved, acknowledged, or supported this. If you decide to try this then you do so at your own risk...... a database
backup is always a good thing. (I know in my case, I will reverse the changes whenever I need to do a WSUS update, and then add them after the update completes.)
The command that stuck out like a sore thumb was "spDeleteUpdate". It appears to execute once for each Windows Update, and it took over one minute to complete. My first change was to add a clustered index to the temporary
table in that stored procedure. I changed this command:
DECLARE @revisionList TABLE(RevisionID INT)
to this:
DECLARE @revisionList TABLE(RevisionID INT Index idx_nc_RevisionID CLUSTERED)
Just making this change improved the query execution. What was talking555,000 reads was now only taking 2000. The table "@revisionList" gets used in searches later in the stored procedure and the index makes it much faster.
Now comes the rest missing indexes which I applied directly to the tables. I added quite of few.... and I realize some are probably not needed, but they certainly didn't hurt. Here are the ones I added:
CREATE INDEX [IXMF_ivwApiUpdateRevision_IsLatestRevision] ON [SUSDB].[dbo].[ivwApiUpdateRevision] ([IsLatestRevision]) INCLUDE ([RevisionID], [IsHidden], [IsLocallyPublished], [IsMandatory]);
CREATE INDEX [IXMF_ivwApiUpdateRevision_IsLatestRevision_IsHidden] ON [SUSDB].[dbo].[ivwApiUpdateRevision] ([IsLatestRevision], [IsHidden]) INCLUDE ([LocalUpdateID], [EffectiveArrivalTime], [RevisionID]);
CREATE INDEX [IXMF_ivwApiUpdateRevision_UpdateID_IsLatestRevision_IsHidden] ON [SUSDB].[dbo].[ivwApiUpdateRevision] ([UpdateID], [IsLatestRevision], [IsHidden]);
CREATE INDEX [IXMF_tbDeadDeployment_TargetGroupID_TargetGroupTypeID_LastChangeNumber] ON [SUSDB].[dbo].[tbDeadDeployment] ([TargetGroupID], [TargetGroupTypeID],[LastChangeNumber]);
CREATE INDEX [IXMF_tbDeadDeployment_TargetGroupTypeID_LastChangeNumber] ON [SUSDB].[dbo].[tbDeadDeployment] ([TargetGroupTypeID], [LastChangeNumber]) INCLUDE ([DeploymentID], [RevisionID], [TargetGroupID], [UpdateID], [RevisionNumber]);
CREATE INDEX [IXMF_tbDeadDeployment_TargetGroupTypeID_LastChangeNumber_UpdateType] ON [SUSDB].[dbo].[tbDeadDeployment] ([TargetGroupTypeID],[LastChangeNumber], [UpdateType]) INCLUDE ([DeploymentID], [RevisionID], [TargetGroupID], [UpdateID], [RevisionNumber]);
CREATE INDEX [IXMF_tbDeadDeployment_TargetGroupTypeID_TimeOfDeath_ActionID] ON [SUSDB].[dbo].[tbDeadDeployment] ([TargetGroupTypeID],[TimeOfDeath], [ActionID]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbDeployment_ActionID_TargetGroupTypeID] ON [SUSDB].[dbo].[tbDeployment] ([ActionID], [TargetGroupTypeID]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbDeployment_DeploymentStatus_TargetGroupTypeID_LastChangeNumber_UpdateType] ON [SUSDB].[dbo].[tbDeployment] ([DeploymentStatus], [TargetGroupTypeID],[LastChangeNumber], [UpdateType]) INCLUDE ([GoLiveTime], [RevisionID], [TargetGroupID]);
CREATE INDEX [IXMF_tbDeployment_TargetGroupTypeID] ON [SUSDB].[dbo].[tbDeployment] ([TargetGroupTypeID]) INCLUDE ([ActionID], [RevisionID]);
CREATE INDEX [IXMF_tbDeployment_TargetGroupTypeID_ActionID] ON [SUSDB].[dbo].[tbDeployment] ([TargetGroupTypeID],[ActionID]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbDeployment_TargetGroupTypeID_LastChangeNumber] ON [SUSDB].[dbo].[tbDeployment] ([TargetGroupTypeID],[LastChangeNumber]) INCLUDE ([DeploymentID], [RevisionID]);
CREATE INDEX [IXMF_tbDeployment_TargetGroupTypeID_LastChangeNumber_UpdateType] ON [SUSDB].[dbo].[tbDeployment] ([TargetGroupTypeID],[LastChangeNumber], [UpdateType]) INCLUDE ([DeploymentID], [DeploymentStatus], [ActionID], [GoLiveTime], [Deadline], [DownloadPriority],
[IsAssigned], [RevisionID], [TargetGroupID], [IsLeaf], [Priority], [IsFeatured], [AutoSelect], [AutoDownload], [SupersedenceBehavior]);
CREATE INDEX [IXMF_tbDeployment_TargetGroupTypeID_UpdateType_LastChangeNumber] ON [SUSDB].[dbo].[tbDeployment] ([TargetGroupTypeID], [UpdateType],[LastChangeNumber]) INCLUDE ([DeploymentID], [ActionID], [Deadline], [RevisionID], [TargetGroupID], [Priority]);
CREATE INDEX [IXMF_tbEventInstance_EventNamespaceID_TimeAtServer] ON [SUSDB].[dbo].[tbEventInstance] ([EventNamespaceID],[TimeAtServer]) INCLUDE ([EventOrdinalNumber]);
CREATE INDEX [IXMF_tbFileOnServer_ActualState] ON [SUSDB].[dbo].[tbFileOnServer] ([ActualState]) INCLUDE ([FileDigest], [RowID], [TimeAddedToQueue]);
CREATE INDEX [IXMF_tbLocalizedPropertyForRevision_LocalizedPropertyID] ON [SUSDB].[dbo].[tbLocalizedPropertyForRevision] ([LocalizedPropertyID]);
CREATE INDEX [IXMF_tbProperty_CreationDate_ReceivedFromCreatorService] ON [SUSDB].[dbo].[tbProperty] ([CreationDate], [ReceivedFromCreatorService]) INCLUDE ([RevisionID], [PublicationState]);
CREATE INDEX [IXMF_tbProperty_ExplicitlyDeployable_UpdateType] ON [SUSDB].[dbo].[tbProperty] ([ExplicitlyDeployable],[UpdateType]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbProperty_PublicationState] ON [SUSDB].[dbo].[tbProperty] ([PublicationState]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbProperty_PublicationState_ReceivedFromCreatorService] ON [SUSDB].[dbo].[tbProperty] ([PublicationState],[ReceivedFromCreatorService]) INCLUDE ([RevisionID], [ExplicitlyDeployable], [UpdateType]);
CREATE INDEX [IXMF_tbRevision_IsLatestRevision] ON [SUSDB].[dbo].[tbRevision] ([IsLatestRevision]) INCLUDE ([RevisionID], [LocalUpdateID]);
CREATE INDEX [IXMF_tbRevision_IsLeaf] ON [SUSDB].[dbo].[tbRevision] ([IsLeaf]) INCLUDE ([RevisionID], [LocalUpdateID]);
CREATE INDEX [IXMF_tbRevision_IsMandatory] ON [SUSDB].[dbo].[tbRevision] ([IsMandatory]) INCLUDE ([LocalUpdateID], [RevisionID]);
CREATE INDEX [IXMF_tbRevision_State] ON [SUSDB].[dbo].[tbRevision] ([State]) INCLUDE ([LocalUpdateID], [RevisionID]);
CREATE INDEX [IXMF_tbRevisionInCategory_CategoryID_Expanded] ON [SUSDB].[dbo].[tbRevisionInCategory] ([CategoryID], [Expanded]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbRevisionInCategory_Expanded] ON [SUSDB].[dbo].[tbRevisionInCategory] ([Expanded]) INCLUDE ([RevisionID], [CategoryID]);
CREATE INDEX [IXMF_tbRevisionSupersedesUpdate_SupersededUpdateID] ON [SUSDB].[dbo].[tbRevisionSupersedesUpdate] ([SupersededUpdateID]);
CREATE INDEX [IXMF_tbUpdate_IsHidden] ON [SUSDB].[dbo].[tbUpdate] ([IsHidden]) INCLUDE ([LocalUpdateID], [UpdateID]);
CREATE INDEX [IXMF_tbUpdate_IsHidden_ImportedTime] ON [SUSDB].[dbo].[tbUpdate] ([IsHidden],[ImportedTime]) INCLUDE ([LocalUpdateID], [UpdateID]);
CREATE INDEX [IXMF_ivwApiUpdateRevision_IsLatestRevision_IsHidden_IsLocallyPublished] ON [SUSDB].[dbo].[ivwApiUpdateRevision] ([IsLatestRevision], [IsHidden], [IsLocallyPublished]);
CREATE INDEX [IXMF_tbDeployment_UpdateType_LastChangeNumber] ON [SUSDB].[dbo].[tbDeployment] ([UpdateType],[LastChangeNumber]) INCLUDE ([RevisionID]);
CREATE INDEX [IXMF_tbFileOnServer_ActualState_DSSRequestedDownload] ON [SUSDB].[dbo].[tbFileOnServer] ([ActualState], [DSSRequestedDownload]) INCLUDE ([FileDigest], [RowID]);
CREATE INDEX [IXMF_tbFileOnServer_DSSRequestedDownload] ON [SUSDB].[dbo].[tbFileOnServer] ([DSSRequestedDownload]) INCLUDE ([FileDigest]);
Note: If you are running these in SQLCMD, make sure you run "set quoted_identifier on" first.
The end result: What took over a minute to execute "spDeleteUpdate" now takes a small fraction of the time. The entire clean-up command completed successfully in a reasonable amount of time, and I got about 60 GB of disk space back.
Hope this helps someone who has ran into a same problem.