Added another benchmark for removing/re-inserting records for cmsContentXml table using one transaction which again improves performance. Now have changed the rebuild xml method in the content service to : Lookup all data that it needs to insert first, then we begin a transaction and inside of the one transaction we clear the data and re-insert it so if anything fails in this process it should be rolled back.

This commit is contained in:
Shannon
2013-07-29 17:45:05 +10:00
parent 82a87cbd92
commit a195d33332
3 changed files with 86 additions and 52 deletions

View File

@@ -39,39 +39,47 @@ namespace Umbraco.Core.Persistence
using (var tr = db.GetTransaction())
{
try
db.BulkInsertRecords(collection, tr);
}
}
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection, Transaction tr)
{
//don't do anything if there are no records.
if (collection.Any() == false)
return;
try
{
if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
{
if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
//SqlCe doesn't support bulk insert statements!
foreach (var poco in collection)
{
//SqlCe doesn't support bulk insert statements!
foreach (var poco in collection)
{
db.Insert(poco);
}
db.Insert(poco);
}
else
}
else
{
string[] sqlStatements;
var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
for (var i = 0; i < sqlStatements.Length; i++)
{
string[] sqlStatements;
var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
for (var i = 0; i < sqlStatements.Length; i++)
using (var cmd = cmds[i])
{
using (var cmd = cmds[i])
{
cmd.CommandText = sqlStatements[i];
cmd.ExecuteNonQuery();
}
cmd.CommandText = sqlStatements[i];
cmd.ExecuteNonQuery();
}
}
}
tr.Complete();
}
catch
{
tr.Dispose();
throw;
}
tr.Complete();
}
catch
{
tr.Dispose();
throw;
}
}

View File

@@ -1378,41 +1378,46 @@ namespace Umbraco.Core.Services
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentRepository(uow))
{
if (contentTypeIds.Any() == false)
{
//Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!)
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
//First we're going to get the data that needs to be inserted before clearing anything, this
//ensures that we don't accidentally leave the content xml table empty if something happens
//during the lookup process.
list.AddRange(GetAllPublished());
}
else
{
foreach (var id in contentTypeIds)
{
//first we'll clear out the data from the cmsContentXml table for this type
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
(select cmsDocument.nodeId from cmsDocument
inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId
where published = 1 and contentType = @contentTypeId)", new {contentTypeId = id});
//now get all published content objects of this type and add to the list
list.AddRange(GetPublishedContentOfContentType(id));
}
}
list.AddRange(contentTypeIds.Any() == false
? GetAllPublished()
: contentTypeIds.SelectMany(GetPublishedContentOfContentType));
var xmlItems = new List<ContentXmlDto>();
foreach (var c in list)
{
//generate the xml
var xml = c.ToXml();
//create the dto to insert
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
xmlItems.Add(new ContentXmlDto {NodeId = c.Id, Xml = xml.ToString(SaveOptions.None)});
}
//bulk insert it into the database
uow.Database.BulkInsertRecords(xmlItems);
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = uow.Database.GetTransaction())
{
if (contentTypeIds.Any() == false)
{
//Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!)
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
}
else
{
foreach (var id in contentTypeIds)
{
//first we'll clear out the data from the cmsContentXml table for this type
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
(select cmsDocument.nodeId from cmsDocument
inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId
where published = 1 and contentType = @contentTypeId)", new { contentTypeId = id });
}
}
//bulk insert it into the database
uow.Database.BulkInsertRecords(xmlItems, tr);
}
}
Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
}

View File

@@ -211,6 +211,27 @@ namespace Umbraco.Tests.Services
}
}
//now, test truncating but then do bulk insertion of records
using (DisposableTimer.DebugDuration<PerformanceTests>("Starting truncate + bulk insert test in one transaction"))
{
//do this 10x!
for (var i = 0; i < 10; i++)
{
//now we insert each record for the ones we've deleted like we do in the content service.
var xmlItems = nodes.Select(node => new ContentXmlDto { NodeId = node.NodeId, Xml = UpdatedXmlStructure }).ToList();
using (var tr = DatabaseContext.Database.GetTransaction())
{
//clear all the xml entries
DatabaseContext.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
INNER JOIN cmsContent ON cmsContentXml.nodeId = cmsContent.nodeId)");
DatabaseContext.Database.BulkInsertRecords(xmlItems, tr);
}
}
}
}