It’s December and we are in a code freeze period at work, usually these period are used to invest in infrastructure upgrades, version upgrades etc.
So this brings me to blog about our upgrade from Sitecore 6.5 to Sitecore 7.2 – this blog post is specifically to document my findings in regards to upgrading Sitecore’s pre-released Item-buckets to their out of the box implementation in more recent versions.
There were a number of approached we discussed in how to go about the Sitecore-Item-Bucket upgrade, here are the two main ones I remember discussing:
- Serialise all item bucket bucket folders and write a parser to upload them after the Sitecore upgrade.
- Unbucket folders, perform Sitecore upgrade and rebucket.
We chose to explore the second option.
First thing to do was take a backup of the production and restore it locally. Then configure a local environment to point to the production replica databases.
From this point I could try to understand what was going on under the hood when attempting to unbucket a folder. I was mainly doing this by having SQL Server Profiler turned on and also by taking counts of all the records in each master database table before and after.
Unbucketing a media item folder
After unbucking a folder and inspecting the SQL Profiler entries I saw statements that indicated deletes were being performed on media items:
exec sp_executesql N’ INSERT INTO [EventQueue]([Id], [EventType],[InstanceType], [InstanceData], [InstanceName], [RaiseLocally], [RaiseGlobally],[UserName]) VALUES( @id, @eventType, @instanceType, @instanceData, @instanceName, @raiseLocally, @raiseGlobally, @userName)’, N’@id uniqueidentifier,@eventType nvarchar(124),@instanceType nvarchar(124),@instanceData nvarchar(205),@instanceName nvarchar(28),@raiseLocally bit,@raiseGlobally bit,@userName nvarchar(18)’,@id=’9092F727-6156-486E-B30F-D834A098BF3F’,@eventType=N’Sitecore.Data.Eventing.Remote.DeletedItemRemoteEvent, Sitecore.Kernel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null’,@instanceType=N’Sitecore.Data.Eventing.Remote.DeletedItemRemoteEvent, Sitecore.Kernel,Version=6.0.0.0,Culture=neutral,PublicKeyToken=null’, @instanceData=N'{“ItemId”:”8b3e7972-19df-423e-81e9-16db6d1d4f81″,”ItemName”:”08″,”LanguageName”:”en”,”TemplateId”:”adb6ca4f-03ef-4f47-b9ac-9ce2ba53ff97″,”VersionNumber”:1, “ParentId”:”ca1119c7-302b-4510-ae67-567c444894dd”}’, @instanceName=N’###’,@raiseLocally=0,@raiseGlobally=1,@userName=N’sitecoreAnonymous’ |
exec sp_executesql N’ DELETE FROM [Items] WHERE [ID] = @itemId DELETE FROM [SharedFields] WHERE [ItemId] = @itemId DELETE FROM [UnversionedFields] WHERE [ItemId] = @itemId DELETE FROM [VersionedFields] WHERE [ItemId] = @itemId’, N’@itemId uniqueidentifier’, @itemId=’CA1119C7-302B-4510-AE67-567C444894DD’ |
This lead to investigating the record counts from the various tables in the master database. I found that the bucketing process was deleting content items from the database but it was leaving the actual media file (stored in the blob table) in-tacked in the database.
You can use this query to obtain record counts of all the tables in a db.
DECLARE @TableRowCounts TABLE ([TableName] VARCHAR(128), [RowCount] INT) ; INSERT INTO @TableRowCounts ([TableName], [RowCount]) EXEC sp_MSforeachtable 'SELECT ''?'' [TableName], COUNT(*) [RowCount] FROM ?' ; SELECT [TableName], [RowCount] FROM @TableRowCounts ORDER BY [TableName]
From this, a script could be created to copy needed records from the backup. The red highlighted tables are the ones that I suspected needed attention and here is the script I pulled together to take data from the backup and create the needed data. Its re-runable, and should allow you to unbucket all need folders and then run the script to re-create everything needed.
NOTE: there is probably a better way to derive the new folder Id for items, it was what I could come up with in the time I had, so please feel free to suggest improvements.
NOTE2: under some situations the unbucketing process worked correctly but under others it didn’t. I didn’t spend time tyring to figure out why.
-- Replace with needed GUIDs DECLARE @ItemBucketTemplateId uniqueidentifier = 'ADB6CA4F-03EF-4F47-B9AC-9CE2BA53FF97' --****************************************************** -- Manage items table --****************************************************** INSERT INTO [CCASitecore_Master].[dbo].[Items] SELECT i.* FROM [CCASitecore_Master_BAK].[dbo].[Items] i INNER JOIN [CCASitecore_Master_BAK].[dbo].[Items] parent ON i.[ParentID] = parent.[ID] WHERE NOT EXISTS ( SELECT * FROM [CCASitecore_Master].[dbo].[Items] newMasterTable WHERE newMasterTable.[ID] = i.[ID]) AND i.[TemplateID] != @ItemBucketTemplateId --****************************************************** -- Fix ParentId for items --****************************************************** UPDATE newItems SET ParentId = oldItems.ParentID FROM [CCASitecore_Master].[dbo].[Items] newItems INNER JOIN [CCASitecore_Master_BAK].[dbo].[Items] oldItems ON newItems.ID = oldItems.ID AND newItems.ParentID != oldItems.ParentID ;WITH Item_ParentFoldersCTE AS ( SELECT ItemsWithDifferentParentIDs.ID 'ItemId', ItemHierarchy.[Ancestor] FROM ( SELECT newItems.ID FROM [CCASitecore_Master].[dbo].[Items] newItems INNER JOIN [CCASitecore_Master_BAK].[dbo].[Items] oldParentItems ON oldParentItems.ID = newItems.ParentID AND oldParentItems.TemplateID = @ItemBucketTemplateId ) ItemsWithDifferentParentIDs INNER JOIN [CCASitecore_Master_BAK].[dbo].[Descendants] ItemHierarchy ON ItemHierarchy.[Descendant] = ItemsWithDifferentParentIDs.[ID] INNER JOIN [CCASitecore_Master_BAK].[dbo].[Items] ItemParents ON ItemHierarchy.[Ancestor] = ItemParents.[ID] AND ItemParents.[TemplateID] != @ItemBucketTemplateId ), Item_ParentFoldersCartesianProductCTE AS ( SELECT List1.[ItemId], List1.[Ancestor] AS 'Ancestor1', list2.[Ancestor] AS 'Ancestor2' FROM Item_ParentFoldersCTE AS List1 CROSS JOIN Item_ParentFoldersCTE AS List2 WHERE List1.[ItemId] = List2.[ItemId] AND List1.[Ancestor] != list2.[Ancestor] ), Item_ParentFolderHierarchyCountCTE AS ( SELECT cp.ItemId, cp.Ancestor1 AS 'ParentId', count(*) AS 'Item_ParentFolderHierarchyCount_Inverse' FROM Item_ParentFoldersCartesianProductCTE cp LEFT OUTER JOIN [CCASitecore_Master_BAK].[dbo].[Descendants] d ON d.[Ancestor] = cp.[Ancestor1] AND d.[Descendant] = cp.[Ancestor2] WHERE d.ID is null GROUP BY cp.ItemId, cp.Ancestor1 ) UPDATE i SET ParentId = r.ParentId FROM ( SELECT ItemId, ParentId, RANK() OVER (PARTITION BY cte.ItemId ORDER BY cte.Item_ParentFolderHierarchyCount_Inverse DESC) AS 'Rank' FROM Item_ParentFolderHierarchyCountCTE cte ) r INNER JOIN [CCASitecore_Master].[dbo].[Items] i ON i.ID = r.ItemId WHERE r.[Rank] = 1 --****************************************************** -- Manage descendants table --****************************************************** INSERT INTO [CCASitecore_Master].[dbo].[Descendants] SELECT DISTINCT d.* FROM [CCASitecore_Master_BAK].[dbo].[Descendants] d LEFT OUTER JOIN [CCASitecore_Master].[dbo].[Items] ItemAncestor ON d.Ancestor = ItemAncestor.ID INNER JOIN [CCASitecore_Master].[dbo].[Items] ItemDescendant ON d.Descendant = ItemDescendant.ID WHERE (ItemAncestor.TemplateID != @ItemBucketTemplateId or d.Ancestor = '00000000-0000-0000-0000-000000000000') AND (ItemDescendant.TemplateID != @ItemBucketTemplateId) AND NOT EXISTS ( SELECT * FROM [CCASitecore_Master].[dbo].[Descendants] d2 WHERE d2.Ancestor = d.Ancestor AND d2.Descendant = d.Descendant) --****************************************************** -- Manage SharedFields table --****************************************************** INSERT INTO [CCASitecore_Master].[dbo].[SharedFields] SELECT sf.* FROM [CCASitecore_Master_BAK].[dbo].[SharedFields] sf INNER JOIN [CCASitecore_Master].[dbo].[Items] i ON sf.FieldId = i.ID WHERE NOT EXISTS ( SELECT * FROM [CCASitecore_Master].[dbo].[SharedFields] sf2 WHERE sf2.ItemId = sf.ItemId AND sf2.FieldId = sf.FieldId) --****************************************************** -- Manage VersionedFields table --****************************************************** INSERT INTO [CCASitecore_Master].[dbo].[VersionedFields] SELECT VF.* FROM [CCASitecore_Master_BAK].[dbo].[VersionedFields] vf INNER JOIN [CCASitecore_Master].[dbo].[Items] i ON vf.FieldId = i.ID WHERE NOT EXISTS ( SELECT * FROM [CCASitecore_Master].[dbo].[VersionedFields] vf2 WHERE vf2.ItemId = vf.ItemId AND vf2.Language = vf.Language AND vf2.Version = vf.Version AND vf2.FieldId = vf.FieldId) --****************************************************** -- Manage UnversionedFields table --****************************************************** INSERT INTO [CCASitecore_Master].[dbo].[UnversionedFields] SELECT VF.* FROM [CCASitecore_Master_BAK].[dbo].[UnversionedFields] vf INNER JOIN [CCASitecore_Master].[dbo].[Items] i ON vf.FieldId = i.ID WHERE NOT EXISTS ( SELECT * FROM [CCASitecore_Master].[dbo].[UnversionedFields] vf2 WHERE vf2.ItemId = vf.ItemId AND vf2.Language = vf.Language AND vf2.FieldId = vf.FieldId)
Unbucketing a content item folder
This was much the same as the media folder unbucketing only that for some reason a far greater amount of items were correctly unbucketed and that there were also records deleted from the UnVersionedFields hence it’s inclusion in the script above.
I found the same script worked for content folders as well as media folders.
So now that I have successfully unbucketed all the necessary folders it was time to follow the standard Sitecore upgrade documents.