public override bool Commit(HashSet <ICmObjectOrSurrogate> newbies, HashSet <ICmObjectOrSurrogate> dirtballs, HashSet <ICmObjectId> goners) { using (m_commitLogMutex.Lock()) { CommitLogMetadata metadata; using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) { metadata = GetMetadata(stream); } List <ICmObjectSurrogate> foreignNewbies; List <ICmObjectSurrogate> foreignDirtballs; List <ICmObjectId> foreignGoners; if (GetUnseenForeignChanges(metadata, out foreignNewbies, out foreignDirtballs, out foreignGoners)) { // we have now seen every commit generation metadata.Peers[m_peerID].Generation = metadata.CurrentGeneration; IUnitOfWorkService uowService = ((IServiceLocatorInternal)m_cache.ServiceLocator).UnitOfWorkService; IReconcileChanges reconciler = uowService.CreateReconciler(foreignNewbies, foreignDirtballs, foreignGoners); if (reconciler.OkToReconcileChanges()) { reconciler.ReconcileForeignChanges(); if (metadata.Master == m_peerID) { var newObjects = new HashSet <ICmObjectOrSurrogate>(foreignNewbies); var editedObjects = new HashSet <ICmObjectOrSurrogate>(foreignDirtballs); var removedObjects = new HashSet <ICmObjectId>(foreignGoners); IEnumerable <CustomFieldInfo> fields; if (HaveAnythingToCommit(newObjects, editedObjects, removedObjects, out fields) && (StartupVersionNumber == ModelVersion)) { PerformCommit(newObjects, editedObjects, removedObjects, fields); } } } else { uowService.ConflictingChanges(reconciler); SaveMetadata(metadata); return(true); } } CheckExitedPeerProcesses(metadata); if (metadata.Master == Guid.Empty) { // Check if the former master left the commit log and XML file in a consistent state. If not, we can't continue. if (metadata.CurrentGeneration != metadata.FileGeneration) { throw new InvalidOperationException("The commit log and XML file are in an inconsistent state."); } base.LockProject(); metadata.Master = m_peerID; } IEnumerable <CustomFieldInfo> cfiList; if (!HaveAnythingToCommit(newbies, dirtballs, goners, out cfiList) && (StartupVersionNumber == ModelVersion)) { SaveMetadata(metadata); return(true); } var commitRec = new CommitLogRecord { Source = m_peerID, WriteGeneration = metadata.CurrentGeneration + 1, ObjectsDeleted = goners.Select(g => g.Guid).ToList(), ObjectsAdded = newbies.Select(n => n.XMLBytes).ToList(), ObjectsUpdated = dirtballs.Select(d => d.XMLBytes).ToList() }; using (var buffer = new MemoryStream()) { Serializer.SerializeWithLengthPrefix(buffer, commitRec, PrefixStyle.Base128, 1); if (metadata.LogLength + buffer.Length > m_settings.SharedXMLBackendCommitLogSize) { // if this peer is the master, then just skip this commit // other peers will not be able to continue when it cannot find the missing commit, but // the master peer can keep going if (metadata.Master != m_peerID) { throw new InvalidOperationException("The current commit cannot be written to the commit log, because it is full."); } } else { byte[] bytes = buffer.GetBuffer(); int commitRecOffset = (metadata.LogOffset + metadata.LogLength) % m_settings.SharedXMLBackendCommitLogSize; // check if the record can fit at the end of the commit log. If not, we wrap around to the beginning. if (commitRecOffset + buffer.Length > m_settings.SharedXMLBackendCommitLogSize) { if (metadata.LogLength == 0) { metadata.LogOffset = 0; } else { metadata.Padding = m_settings.SharedXMLBackendCommitLogSize - commitRecOffset; } metadata.LogLength += metadata.Padding; commitRecOffset = 0; } using (MemoryMappedViewStream stream = m_commitLog.CreateViewStream(commitRecOffset, buffer.Length)) { stream.Write(bytes, 0, (int)buffer.Length); metadata.LogLength += (int)buffer.Length; } } } if (metadata.Master == m_peerID) { PerformCommit(newbies, dirtballs, goners, cfiList); } metadata.CurrentGeneration++; // we've seen our own change metadata.Peers[m_peerID].Generation = metadata.CurrentGeneration; SaveMetadata(metadata); return(true); } }
public override bool Commit(HashSet<ICmObjectOrSurrogate> newbies, HashSet<ICmObjectOrSurrogate> dirtballs, HashSet<ICmObjectId> goners) { using (m_commitLogMutex.Lock()) { CommitLogMetadata metadata; using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) { metadata = GetMetadata(stream); } List<ICmObjectSurrogate> foreignNewbies; List<ICmObjectSurrogate> foreignDirtballs; List<ICmObjectId> foreignGoners; if (GetUnseenForeignChanges(metadata, out foreignNewbies, out foreignDirtballs, out foreignGoners)) { // we have now seen every commit generation metadata.Peers[m_peerID].Generation = metadata.CurrentGeneration; IUnitOfWorkService uowService = ((IServiceLocatorInternal) m_cache.ServiceLocator).UnitOfWorkService; IReconcileChanges reconciler = uowService.CreateReconciler(foreignNewbies, foreignDirtballs, foreignGoners); if (reconciler.OkToReconcileChanges()) { reconciler.ReconcileForeignChanges(); if (metadata.Master == m_peerID) { var newObjects = new HashSet<ICmObjectOrSurrogate>(foreignNewbies); var editedObjects = new HashSet<ICmObjectOrSurrogate>(foreignDirtballs); var removedObjects = new HashSet<ICmObjectId>(foreignGoners); IEnumerable<CustomFieldInfo> fields; if (HaveAnythingToCommit(newObjects, editedObjects, removedObjects, out fields) && (StartupVersionNumber == ModelVersion)) PerformCommit(newObjects, editedObjects, removedObjects, fields); } } else { uowService.ConflictingChanges(reconciler); SaveMetadata(metadata); return true; } } CheckExitedPeerProcesses(metadata); if (metadata.Master == Guid.Empty) { // Check if the former master left the commit log and XML file in a consistent state. If not, we can't continue. if (metadata.CurrentGeneration != metadata.FileGeneration) throw new InvalidOperationException("The commit log and XML file are in an inconsistent state."); base.LockProject(); metadata.Master = m_peerID; } IEnumerable<CustomFieldInfo> cfiList; if (!HaveAnythingToCommit(newbies, dirtballs, goners, out cfiList) && (StartupVersionNumber == ModelVersion)) { SaveMetadata(metadata); return true; } var commitRec = new CommitLogRecord { Source = m_peerID, WriteGeneration = metadata.CurrentGeneration + 1, ObjectsDeleted = goners.Select(g => g.Guid).ToList(), ObjectsAdded = newbies.Select(n => n.XMLBytes).ToList(), ObjectsUpdated = dirtballs.Select(d => d.XMLBytes).ToList() }; using (var buffer = new MemoryStream()) { Serializer.SerializeWithLengthPrefix(buffer, commitRec, PrefixStyle.Base128, 1); if (metadata.LogLength + buffer.Length > m_settings.SharedXMLBackendCommitLogSize) { // if this peer is the master, then just skip this commit // other peers will not be able to continue when it cannot find the missing commit, but // the master peer can keep going if (metadata.Master != m_peerID) throw new InvalidOperationException("The current commit cannot be written to the commit log, because it is full."); } else { byte[] bytes = buffer.GetBuffer(); int commitRecOffset = (metadata.LogOffset + metadata.LogLength) % m_settings.SharedXMLBackendCommitLogSize; // check if the record can fit at the end of the commit log. If not, we wrap around to the beginning. if (commitRecOffset + buffer.Length > m_settings.SharedXMLBackendCommitLogSize) { if (metadata.LogLength == 0) metadata.LogOffset = 0; else metadata.Padding = m_settings.SharedXMLBackendCommitLogSize - commitRecOffset; metadata.LogLength += metadata.Padding; commitRecOffset = 0; } using (MemoryMappedViewStream stream = m_commitLog.CreateViewStream(commitRecOffset, buffer.Length)) { stream.Write(bytes, 0, (int) buffer.Length); metadata.LogLength += (int) buffer.Length; } } } if (metadata.Master == m_peerID) PerformCommit(newbies, dirtballs, goners, cfiList); metadata.CurrentGeneration++; // we've seen our own change metadata.Peers[m_peerID].Generation = metadata.CurrentGeneration; SaveMetadata(metadata); return true; } }