private static void RemovePeer(CommitLogMetadata metadata, Guid peerID) { metadata.Peers.Remove(peerID); if (metadata.Master == peerID) { metadata.Master = Guid.Empty; } }
protected override int StartupInternal(int currentModelVersion) { CreateSettingsStores(); m_commitLogMutex = new GlobalMutex(MutexName); bool createdNew; using (m_commitLogMutex.InitializeAndLock(out createdNew)) { CreateSharedMemory(createdNew); using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) { CommitLogMetadata metadata = null; if (!createdNew) { if (TryGetMetadata(stream, out metadata)) { CheckExitedPeerProcesses(metadata); if (m_peerProcesses.Count == 0) { createdNew = true; } } else { createdNew = true; } } if (createdNew) { metadata = new CommitLogMetadata(); } using (Process curProcess = Process.GetCurrentProcess()) metadata.Peers[m_peerID] = new CommitLogPeer { ProcessID = curProcess.Id, Generation = metadata.FileGeneration }; if (metadata.Master == Guid.Empty) { base.LockProject(); metadata.Master = m_peerID; } int startupModelVersion = ReadInSurrogates(currentModelVersion); // non-master peers cannot migrate the XML file if (startupModelVersion < currentModelVersion && metadata.Master != m_peerID) { throw new LcmDataMigrationForbiddenException(); } SaveMetadata(stream, metadata); return(startupModelVersion); } } }
protected override void WriteCommitWork(CommitWork workItem) { using (m_commitLogMutex.Lock()) { base.WriteCommitWork(workItem); using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) { CommitLogMetadata metadata = GetMetadata(stream); metadata.FileGeneration = metadata.Peers[m_peerID].Generation; SaveMetadata(stream, metadata); } } }
private static bool TryGetMetadata(MemoryMappedViewStream stream, out CommitLogMetadata metadata) { stream.Seek(0, SeekOrigin.Begin); int length; if (Serializer.TryReadLengthPrefix(stream, PrefixStyle.Base128, out length) && length > 0) { stream.Seek(0, SeekOrigin.Begin); metadata = Serializer.DeserializeWithLengthPrefix <CommitLogMetadata>(stream, PrefixStyle.Base128, 1); return(true); } metadata = null; return(false); }
internal override void LockProject() { using (m_commitLogMutex.Lock()) { base.LockProject(); using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) { CommitLogMetadata metadata = GetMetadata(stream); if (metadata.Master == Guid.Empty) { metadata.Master = m_peerID; SaveMetadata(stream, metadata); } } } }
/// <summary> /// Checks for peer processes that have exited unexpectedly and update the metadata accordingly. /// </summary> private bool CheckExitedPeerProcesses(CommitLogMetadata metadata) { bool changed = false; var processesToRemove = new HashSet <Process>(m_peerProcesses.Values); foreach (KeyValuePair <Guid, CommitLogPeer> kvp in metadata.Peers.ToArray()) { if (kvp.Key == m_peerID) { continue; } Process process; if (m_peerProcesses.TryGetValue(kvp.Value.ProcessID, out process)) { if (process.HasExited) { RemovePeer(metadata, kvp.Key); changed = true; } else { processesToRemove.Remove(process); } } else { try { process = Process.GetProcessById(kvp.Value.ProcessID); m_peerProcesses[kvp.Value.ProcessID] = process; } catch (ArgumentException) { RemovePeer(metadata, kvp.Key); changed = true; } } } foreach (Process process in processesToRemove) { m_peerProcesses.Remove(process.Id); process.Close(); } return(changed); }
private int ReadUnseenCommitRecords(CommitLogMetadata metadata, int minPeerGeneration, int startOffset, int length, List <CommitLogRecord> unseenCommits) { if (length == 0) { return(0); } int generation = metadata.Peers[m_peerID].Generation; using (MemoryMappedViewStream stream = m_commitLog.CreateViewStream(startOffset, length)) { while (stream.Position < length) { long startPos = stream.Position; var rec = Serializer.DeserializeWithLengthPrefix <CommitLogRecord>(stream, PrefixStyle.Base128, 1); if (rec.WriteGeneration > generation && rec.Source != m_peerID) { unseenCommits.Add(rec); } // remove the record from the commit log once all peers have seen it and it has been written to disk if (rec.WriteGeneration <= minPeerGeneration && rec.WriteGeneration <= metadata.FileGeneration) { metadata.LogOffset = startOffset + (int)stream.Position; metadata.LogLength -= (int)(stream.Position - startPos); } } } // if we have read everything to the end of the file, add padding to read length if (startOffset + length == m_settings.SharedXMLBackendCommitLogSize - metadata.Padding) { length += metadata.Padding; } // check if we've purged all records up to the end of the file. If so, wrap around to the beginning. if (metadata.LogOffset == m_settings.SharedXMLBackendCommitLogSize - metadata.Padding) { metadata.LogOffset = 0; metadata.LogLength -= metadata.Padding; metadata.Padding = 0; } return(length); }
protected override void CreateInternal() { m_commitLogMutex = new GlobalMutex(MutexName); using (m_commitLogMutex.InitializeAndLock()) { CreateSharedMemory(true); var metadata = new CommitLogMetadata { Master = m_peerID }; using (Process curProcess = Process.GetCurrentProcess()) metadata.Peers[m_peerID] = new CommitLogPeer { ProcessID = curProcess.Id, Generation = metadata.FileGeneration }; using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) { SaveMetadata(stream, metadata); } base.CreateInternal(); } }
/// <summary> /// Gets all unseen foreign changes from the commit log. The metadata should be saved after calling this method, /// because inactive records might have been purged. /// </summary> 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); }
private void SaveMetadata(CommitLogMetadata metadata) { using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream()) SaveMetadata(stream, metadata); }
private static void SaveMetadata(MemoryMappedViewStream stream, CommitLogMetadata metadata) { stream.Seek(0, SeekOrigin.Begin); Serializer.SerializeWithLengthPrefix(stream, metadata, PrefixStyle.Base128, 1); }