protected override int StartupInternal(int currentModelVersion)
		{
			bool createdNew;
			m_commitLogMutex = new Mutex(true, MutexName, out createdNew);
			if (!createdNew)
				m_commitLogMutex.WaitOne();
			try
			{
				CreateSharedMemory(createdNew);
				using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
				{
					CommitLogMetadata metadata;
					if (!createdNew && TryGetMetadata(stream, out metadata))
						CheckExitedPeerProcesses(metadata);
					else
						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 FdoDataMigrationForbiddenException();

					SaveMetadata(stream, metadata);

					return startupModelVersion;
				}
			}
			finally
			{
				m_commitLogMutex.ReleaseMutex();
			}
		}
		protected override int StartupInternal(int currentModelVersion)
		{
			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 FdoDataMigrationForbiddenException();

					SaveMetadata(stream, metadata);

					return startupModelVersion;
				}
			}
		}
		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;
		}
		/// <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 static void RemovePeer(CommitLogMetadata metadata, Guid peerID)
		{
			metadata.Peers.Remove(peerID);
			if (metadata.Master == peerID)
				metadata.Master = Guid.Empty;
		}
		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 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);
		}
		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;
		}
		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();
			}
		}
Пример #11
0
		protected override void CreateInternal()
		{
			bool createdNew;
			m_commitLogMutex = new Mutex(true, MutexName, out createdNew);
			if (!createdNew)
			{
				// Mono does not close the named mutex handle until the process exits, this can cause problems
				// for some unit tests, so we disable the following check for Mono
#if !__MonoCS__
				throw new InvalidOperationException("Cannot create shared XML backend.");
#else
				m_commitLogMutex.WaitOne();
#endif
			}
			try
			{
				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();
			}
			finally
			{
				m_commitLogMutex.ReleaseMutex();
			}
		}