예제 #1
0
 private static void RemovePeer(CommitLogMetadata metadata, Guid peerID)
 {
     metadata.Peers.Remove(peerID);
     if (metadata.Master == peerID)
     {
         metadata.Master = Guid.Empty;
     }
 }
예제 #2
0
        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);
                }
            }
        }
예제 #3
0
        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);
                }
            }
        }
예제 #4
0
        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);
        }
예제 #5
0
 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);
             }
         }
     }
 }
예제 #6
0
        /// <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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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();
            }
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
 private void SaveMetadata(CommitLogMetadata metadata)
 {
     using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
         SaveMetadata(stream, metadata);
 }
예제 #11
0
 private static void SaveMetadata(MemoryMappedViewStream stream, CommitLogMetadata metadata)
 {
     stream.Seek(0, SeekOrigin.Begin);
     Serializer.SerializeWithLengthPrefix(stream, metadata, PrefixStyle.Base128, 1);
 }