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;
}
}