internal static void SaveNodeData(Node node, NodeSaveSettings settings, IIndexPopulator populator, string originalPath, string newPath) { var isNewNode = node.Id == 0; var data = node.Data; var deadlockCount = 0; var isDeadlock = false; while (deadlockCount++ < maxDeadlockIterations) { var stopper = Stopwatch.StartNew(); isDeadlock = !SaveNodeDataTransactional(node, settings, populator, originalPath, newPath); Debug.WriteLine("<? SaveNodeDataTransactional: " + node.Path + ", " + stopper.Elapsed); stopper.Stop(); if (!isDeadlock) { break; } Logger.WriteWarning(Logger.EventId.NotDefined, "Deadlock detected in SaveNodeData", properties: new Dictionary <string, object> { { "Id: ", node.Id }, { "Path: ", node.Path }, { "Version: ", node.Version } }); System.Threading.Thread.Sleep(sleepIfDeadlock); } if (isNewNode) { Logger.WriteVerbose("Node created.", CollectLoggerProperties, data); } else { Logger.WriteVerbose("Node updated.", CollectLoggerProperties, data); } }
private static Exception SaveNodeDataTransactional(Node node, NodeSaveSettings settings, IIndexPopulator populator, string originalPath, string newPath) { IndexDocumentData indexDocument = null; bool hasBinary = false; var data = node.Data; var isNewNode = data.Id == 0; NodeDataParticipant participant = null; var msg = "Saving Node#" + node.Id + ", " + node.ParentPath + "/" + node.Name; var isLocalTransaction = !TransactionScope.IsActive; using (var op = SnTrace.Database.StartOperation(msg)) { if (isLocalTransaction) { TransactionScope.Begin(); } try { // collect data for populator var populatorData = populator.BeginPopulateNode(node, settings, originalPath, newPath); data.CreateSnapshotData(); participant = new NodeDataParticipant { Data = data, Settings = settings, IsNewNode = isNewNode }; TransactionScope.Participate(participant); if (settings.NodeHead != null) { settings.LastMajorVersionIdBefore = settings.NodeHead.LastMajorVersionId; settings.LastMinorVersionIdBefore = settings.NodeHead.LastMinorVersionId; } int lastMajorVersionId, lastMinorVersionId; DataProvider.Current.SaveNodeData(data, settings, out lastMajorVersionId, out lastMinorVersionId); settings.LastMajorVersionIdAfter = lastMajorVersionId; settings.LastMinorVersionIdAfter = lastMinorVersionId; // here we re-create the node head to insert it into the cache and refresh the version info); if (lastMajorVersionId > 0 || lastMinorVersionId > 0) { var head = NodeHead.CreateFromNode(node, lastMinorVersionId, lastMajorVersionId); if (MustCache(node.NodeType)) { // participate cache items var idKey = CreateNodeHeadIdCacheKey(head.Id); var participant2 = new InsertCacheParticipant { CacheKey = idKey }; TransactionScope.Participate(participant2); var pathKey = CreateNodeHeadPathCacheKey(head.Path); var participant3 = new InsertCacheParticipant { CacheKey = pathKey }; TransactionScope.Participate(participant3); CacheNodeHead(head, idKey, pathKey); } node.RefreshVersionInfo(head); if (!settings.DeletableVersionIds.Contains(node.VersionId)) { // Elevation: we need to create the index document with full // control to avoid field access errors (indexing must be independent // from the current users permissions). using (new SystemAccount()) { indexDocument = SaveIndexDocument(node, true, isNewNode, out hasBinary); } } } if (isLocalTransaction) { TransactionScope.Commit(); } // populate index only if it is enabled on this content (e.g. preview images will be skipped) using (var op2 = SnTrace.Index.StartOperation("Indexing node")) { if (node.IsIndexingEnabled) { using (new SystemAccount()) populator.CommitPopulateNode(populatorData, indexDocument); } if (indexDocument != null && hasBinary) { using (new SystemAccount()) { indexDocument = SaveIndexDocument(node, indexDocument); populator.FinalizeTextExtracting(populatorData, indexDocument); } } op2.Successful = true; } } catch (NodeIsOutOfDateException) { RemoveFromCache(participant); throw; } catch (System.Data.Common.DbException dbe) { if (isLocalTransaction && IsDeadlockException(dbe)) { return(dbe); } throw SavingExceptionHelper(data, dbe); } catch (Exception e) { var ee = SavingExceptionHelper(data, e); if (ee == e) { throw; } else { throw ee; } } finally { if (isLocalTransaction && TransactionScope.IsActive) { TransactionScope.Rollback(); } } op.Successful = true; } return(null); }
internal static void SaveNodeData(Node node, NodeSaveSettings settings, IIndexPopulator populator, string originalPath, string newPath) { var isNewNode = node.Id == 0; var isOwnerChanged = node.Data.IsPropertyChanged("OwnerId"); if (!isNewNode && isOwnerChanged) { node.Security.Assert(PermissionType.TakeOwnership); } var data = node.Data; var attempt = 0; using (var op = SnTrace.Database.StartOperation("SaveNodeData")) { while (true) { attempt++; var deadlockException = SaveNodeDataTransactional(node, settings, populator, originalPath, newPath); if (deadlockException == null) { break; } SnTrace.Database.Write("DEADLOCK detected. Attempt: {0}/{1}, NodeId:{2}, Version:{3}, Path:{4}", attempt, maxDeadlockIterations, node.Id, node.Version, node.Path); if (attempt >= maxDeadlockIterations) { throw new Exception(string.Format("Error saving node. Id: {0}, Path: {1}", node.Id, node.Path), deadlockException); } SnLog.WriteWarning("Deadlock detected in SaveNodeData", properties: new Dictionary <string, object> { { "Id: ", node.Id }, { "Path: ", node.Path }, { "Version: ", node.Version }, { "Attempt: ", attempt } }); System.Threading.Thread.Sleep(sleepIfDeadlock); } op.Successful = true; } try { if (isNewNode) { SecurityHandler.CreateSecurityEntity(node.Id, node.ParentId, node.OwnerId); } else if (isOwnerChanged) { SecurityHandler.ModifyEntityOwner(node.Id, node.OwnerId); } } catch (EntityNotFoundException e) { SnLog.WriteException(e, $"Error during creating or modifying security entity: {node.Id}. Original message: {e}", EventId.Security); } catch (SecurityStructureException) // suppressed { // no need to log this: somebody else already created or modified this security entity } if (isNewNode) { SnTrace.ContentOperation.Write("Node created. Id:{0}, Path:{1}", data.Id, data.Path); } else { SnTrace.ContentOperation.Write("Node updated. Id:{0}, Path:{1}", data.Id, data.Path); } }
private static bool SaveNodeDataTransactional(Node node, NodeSaveSettings settings, IIndexPopulator populator, string originalPath, string newPath) { IndexDocumentData indexDocument = null; bool hasBinary = false; var data = node.Data; var isNewNode = data.Id == 0; NodeDataParticipant participant = null; var isLocalTransaction = !TransactionScope.IsActive; if (isLocalTransaction) { TransactionScope.Begin(); } Logger.WriteVerbose("Transaction: " + TransactionScope.CurrentId + " SAVING " + node.Id + ", " + node.Path); try { //-- collect data for populator var populatorData = populator.BeginPopulateNode(node, settings, originalPath, newPath); data.CreateSnapshotData(); participant = new NodeDataParticipant { Data = data, Settings = settings, IsNewNode = isNewNode }; TransactionScope.Participate(participant); int lastMajorVersionId, lastMinorVersionId; DataProvider.Current.SaveNodeData(data, settings, out lastMajorVersionId, out lastMinorVersionId); //-- here we re-create the node head to insert it into the cache and refresh the version info); if (lastMajorVersionId > 0 || lastMinorVersionId > 0) { var head = NodeHead.CreateFromNode(node, lastMinorVersionId, lastMajorVersionId); if (MustCache(node.NodeType)) { //-- participate cache items var idKey = CreateNodeHeadIdCacheKey(head.Id); var participant2 = new InsertCacheParticipant { CacheKey = idKey }; TransactionScope.Participate(participant2); var pathKey = CreateNodeHeadPathCacheKey(head.Path); var participant3 = new InsertCacheParticipant { CacheKey = pathKey }; TransactionScope.Participate(participant3); CacheNodeHead(head, idKey, pathKey); } node.RefreshVersionInfo(head); if (!settings.DeletableVersionIds.Contains(node.VersionId)) { // Elevation: we need to create the index document with full // control to avoid field access errors (indexing must be independent // from the current users permissions). using (new SystemAccount()) { indexDocument = SaveIndexDocument(node, true, out hasBinary); } } } if (isLocalTransaction) { TransactionScope.Commit(); } //-- populate using (new SystemAccount()) populator.CommitPopulateNode(populatorData, indexDocument); if (indexDocument != null && hasBinary) { using (new SystemAccount()) { indexDocument = SaveIndexDocument(node, indexDocument); populator.FinalizeTextExtracting(populatorData, indexDocument); } } } catch (NodeIsOutOfDateException) { RemoveFromCache(participant); throw; } catch (System.Data.Common.DbException dbe) { Logger.WriteException(new Exception(string.Format("Error saving node. Id: {0}, Path: {1}", node.Id, node.Path), dbe)); if (isLocalTransaction && IsDeadlockException(dbe)) { return(false); } throw SavingExceptionHelper(data, dbe); } catch (Exception e) { Logger.WriteException(new Exception(string.Format("Error saving node. Id: {0}, Path: {1}", node.Id, node.Path), e)); var ee = SavingExceptionHelper(data, e); if (ee == e) { throw; } else { throw ee; } } finally { if (isLocalTransaction && TransactionScope.IsActive) { TransactionScope.Rollback(); } } return(true); }
public static IRepositoryBuilder UseIndexPopulator(this IRepositoryBuilder repositoryBuilder, IIndexPopulator indexPopulator) { return(repositoryBuilder); }