private bool GetUnseenForeignChanges(CommitLogMetadata metadata,
out List<ICmObjectSurrogate> foreignNewbies,
out List<ICmObjectSurrogate> foreignDirtballs,
out List<ICmObjectId> foreignGoners)
{
foreignNewbies = new List<ICmObjectSurrogate>();
foreignDirtballs = new List<ICmObjectSurrogate>();
foreignGoners = new List<ICmObjectId>();
int minPeerGeneration = metadata.Peers.Select(p => p.Key == m_peerID ? metadata.CurrentGeneration : p.Value.Generation).Min();
var unseenCommitRecs = new List<CommitLogRecord>();
int bytesRemaining = metadata.LogLength;
// read all records up to the end of the file or the end of the log, whichever comes first
int length = Math.Min(metadata.LogLength, m_settings.SharedXMLBackendCommitLogSize - metadata.LogOffset - metadata.Padding);
bytesRemaining -= ReadUnseenCommitRecords(metadata, minPeerGeneration, metadata.LogOffset, length, unseenCommitRecs);
// if there are bytes remaining, it means that we hit the end of the file, so we need to wrap around to the beginning
if (bytesRemaining > 0)
bytesRemaining -= ReadUnseenCommitRecords(metadata, minPeerGeneration, 0, bytesRemaining, unseenCommitRecs);
Debug.Assert(bytesRemaining == 0);
if (unseenCommitRecs.Count == 0)
return false;
// check if there was enough room in the commit log for the last peer to write its commit
// if it was not able, then we cannot continue, because we will be out-of-sync
if (unseenCommitRecs[unseenCommitRecs.Count - 1].WriteGeneration < metadata.CurrentGeneration)
throw new InvalidOperationException("The most recent unseen commit could not be found.");
var idFactory = m_cache.ServiceLocator.GetInstance<ICmObjectIdFactory>();
var newbies = new Dictionary<Guid, ICmObjectSurrogate>();
var dirtballs = new Dictionary<Guid, ICmObjectSurrogate>();
var goners = new HashSet<Guid>();
var surrogateFactory = m_cache.ServiceLocator.GetInstance<ICmObjectSurrogateFactory>();
foreach (CommitLogRecord commitRec in unseenCommitRecs)
{
if (commitRec.ObjectsDeleted != null)
{
foreach (Guid goner in commitRec.ObjectsDeleted)
{
// If it was created by a previous foreign change we haven't seen, we can just forget it.
if (newbies.Remove(goner))
continue;
// If it was modified by a previous foreign change we haven't seen, we can forget the modification.
// (but we still need to know it's gone).
dirtballs.Remove(goner);
goners.Add(goner);
}
}
if (commitRec.ObjectsUpdated != null)
{
foreach (byte[] dirtballXml in commitRec.ObjectsUpdated)
{
ICmObjectSurrogate dirtballSurrogate = surrogateFactory.Create(dirtballXml);
// This shouldn't be necessary; if a previous foreign transaction deleted it, it
// should not show up as a dirtball in a later transaction until it has shown up as a newby.
// goners.Remove(dirtball);
// If this was previously known as a newby or modified, then to us it still is.
if (newbies.ContainsKey(dirtballSurrogate.Guid) || dirtballs.ContainsKey(dirtballSurrogate.Guid))
continue;
dirtballs[dirtballSurrogate.Guid] = dirtballSurrogate;
}
}
if (commitRec.ObjectsAdded != null)
{
foreach (byte[] newbyXml in commitRec.ObjectsAdded)
{
ICmObjectSurrogate newObj = surrogateFactory.Create(newbyXml);
if (goners.Remove(newObj.Guid))
{
// an object which an earlier transaction deleted is being re-created.
// This means that to us, it is a dirtball.
dirtballs[newObj.Guid] = newObj;
continue;
}
// It shouldn't be in dirtballs; can't be new in one transaction without having been deleted previously.
// So it really is new.
newbies[newObj.Guid] = newObj;
}
}
foreignNewbies.AddRange(newbies.Values);
foreignDirtballs.AddRange(dirtballs.Values);
foreignGoners.AddRange(from guid in goners select idFactory.FromGuid(guid));
}
return true;
}