public JournalEntry(JournalFile file, string resourceName, long position, long pageNumber) { File = file; ResourceName = resourceName; Position = position; PageNumber = pageNumber; }
public void SetCheckPoint(bool flushJournals) { // No Logging if (!EnableLogging) { return; } // Return if Read-only if (ReadOnly) { return; } bool persisting; lock (topJournalLock) { JournalFile topJ = TopJournal; // When the journal exceeds a threshold then we cycle the top journal if (flushJournals || topJ.Length > (256 * 1024)) { // Cycle to the next journal file NewTopJournalFile(); // Add this to the archives journalArchives.Add(topJ); } persisting = journalArchives.Count > 0; topJ.SetCheckPoint(); } if (persisting) { // Notifies the background thread that there is something to persist. // This will block until there are at most 10 journal files open. journalingThread.PersistArchives(10); } }
public JournalSummary(JournalFile journalFile) { JournalFile = journalFile; Resources = new List <string>(); }
public override void Read(long pageNumber, byte[] buffer, int offset) { lock (journalMap) { if (!dataOpen) { throw new IOException("Assertion failed: Data file is not open."); } } // The list of all journal entries on this page number var allJournalEntries = new List <JournalEntry>(4); try { // The map index. lock (journalMap) { int i = ((int)(pageNumber & 0x0FFFFFFF) % journalMap.Length); var entry = journalMap[i]; JournalEntry prev = null; while (entry != null) { bool deletedHash = false; JournalFile file = entry.File; // Note that once we have a reference the journal file can not be // deleted. file.Reference(); // If the file is closed (or deleted) if (file.IsDeleted) { deletedHash = true; // Deleted so remove the reference to the journal file.Dereference(); // Remove the journal entry from the chain. if (prev == null) { journalMap[i] = entry.Next; } else { prev.Next = entry.Next; } } // Else if not closed then is this entry the page number? else if (entry.PageNumber == pageNumber) { allJournalEntries.Add(entry); } else { // Not the page we are looking for so remove the reference to the // file. file.Dereference(); } // Only move prev is we have NOT deleted a hash entry if (!deletedHash) { prev = entry; } entry = entry.Next; } } // Read any data from the underlying file if (thereIsBackingData) { long pagePosition = pageNumber * JournaledSystem.PageSize; // First Read the page from the underlying store. Data.Read(pagePosition, buffer, offset, JournaledSystem.PageSize); } else { // Clear the buffer for (int i = offset; i < (JournaledSystem.PageSize + offset); ++i) { buffer[i] = 0; } } // Rebuild from the journal file(s) int sz = allJournalEntries.Count; for (int i = 0; i < sz; ++i) { var entry = allJournalEntries[i]; var file = entry.File; long position = entry.Position; lock (file) { file.BuildPage(pageNumber, position, buffer, offset); } } } finally { // Make sure we remove the reference for all the journal files. int sz = allJournalEntries.Count; for (int i = 0; i < sz; ++i) { var entry = allJournalEntries[i]; JournalFile file = entry.File; file.Dereference(); } } }
public override void Write(long pageNumber, byte[] buffer, int offset, int count) { lock (journalMap) { if (!dataOpen) { throw new IOException("Assertion failed: Data file is not open."); } // Make this modification input the log var journal = JournaledSystem.LogPageModification(Name, pageNumber, buffer, offset, count); // This adds the modification to the END of the hash list. This means // when we reconstruct the page the journals will always be input the // correct order - from oldest to newest. // The map index. int i = ((int)(pageNumber & 0x0FFFFFFF) % journalMap.Length); var entry = journalMap[i]; // Make sure this entry is added to the END if (entry == null) { // Add at the head if no first entry journalMap[i] = journal; journal.Next = null; } else { // Otherwise search to the end // The number of journal entries input the linked list int journalEntryCount = 0; while (entry.Next != null) { entry = entry.Next; ++journalEntryCount; } // and add to the end entry.Next = journal; journal.Next = null; // If there are over 35 journal entries, scan and remove all entries // on journals that have persisted if (journalEntryCount > 35) { entry = journalMap[i]; JournalEntry prev = null; while (entry != null) { bool deletedHash = false; JournalFile file = entry.File; // Note that once we have a reference the journal file can not be // deleted. file.Reference(); // If the file is closed (or deleted) if (file.IsDeleted) { deletedHash = true; // Deleted so remove the reference to the journal file.Dereference(); // Remove the journal entry from the chain. if (prev == null) { journalMap[i] = entry.Next; } else { prev.Next = entry.Next; } } // Remove the reference file.Dereference(); // Only move prev is we have NOT deleted a hash entry if (!deletedHash) { prev = entry; } entry = entry.Next; } } } } }
public JournalSummary(JournalFile journalFile) { JournalFile = journalFile; Resources = new List<string>(); }
private void Execute() { bool localFinished = false; while (!localFinished) { List <JournalFile> toProcess = null; lock (system.topJournalLock) { if (system.journalArchives.Count > 0) { toProcess = new List <JournalFile>(); toProcess.AddRange(system.journalArchives); } } if (toProcess == null) { // Nothing to process so wait lock (this) { if (!finished) { #if PCL try { Monitor.Wait(this); } catch (OperationCanceledException) { } #else try { Monitor.Wait(this); } catch (ThreadInterruptedException) { /* ignore */ } #endif } } } else if (toProcess.Count > 0) { // Something to process, so go ahead and process the journals, int sz = toProcess.Count; // For all journals for (int i = 0; i < sz; ++i) { // Pick the lowest journal to persist JournalFile jf = toProcess[i]; try { // Persist the journal jf.Persist(8, jf.Length); // Close and then delete the journal file jf.CloseAndDelete(); } catch (IOException e) { system.Context.OnError(String.Format("Error persisting journal '{0}", jf), e); // If there is an error persisting the best thing to do is // finish lock (this) { finished = true; } } } } lock (this) { localFinished = finished; // Remove the journals that we have just persisted. if (toProcess != null) { lock (system.topJournalLock) { int sz = toProcess.Count; for (int i = 0; i < sz; ++i) { system.journalArchives.RemoveAt(0); } } } // Notify any threads waiting Monitor.PulseAll(this); } } lock (this) { actually_finished = true; Monitor.PulseAll(this); } }
private void RollForwardRecover() { // The list of all journal files, var journalFilesList = new List <JournalSummary>(); // Scan the journal path for any journal files. for (int i = 10; i < 74; ++i) { string journalFn = GetJournalFileName(i); string f = FileSystem.CombinePath(JournalPath, journalFn); // If the journal exists, create a summary of the journal if (FileSystem.FileExists(f)) { if (ReadOnly) { throw new IOException( "Journal file " + f + " exists for a read-only session. " + "There may not be any pending journals for a Read-only session."); } var jf = new JournalFile(this, FileSystem, f, ReadOnly); // Open the journal file for recovery. This will set various // information about the journal such as the last check point and the // id of the journal file. JournalSummary summary = jf.OpenForRecovery(); // If the journal can be recovered from. if (summary.CanBeRecovered) { Context.OnInformation(String.Format("Journal '{0}' found: can be recovered", jf)); journalFilesList.Add(summary); } else { Context.OnInformation(String.Format("Deleting journal '{0}': nothing to recover", jf)); // Otherwise close and delete it jf.CloseAndDelete(); } } } // Sort the journal file list from oldest to newest. The oldest journals // are recovered first. journalFilesList.Sort(new JournalSummaryComparer()); long lastJournalNumber = -1; // Persist the journals for (int i = 0; i < journalFilesList.Count; ++i) { var summary = journalFilesList[i]; // Check the resources for this summary var resList = summary.Resources; foreach (string resourceName in resList) { // This puts the resource into the hash map. CreateResource(resourceName); } // Assert that we are recovering the journals input the correct order JournalFile jf = summary.JournalFile; if (jf.JournalNumber < lastJournalNumber) { throw new InvalidOperationException("Assertion failed, sort failed."); } lastJournalNumber = jf.JournalNumber; Context.OnInformation(String.Format("Recovering '{0}' (8 .. {1})", jf, summary.LastCheckPoint)); jf.Persist(8, summary.LastCheckPoint); // Then close and delete. jf.CloseAndDelete(); // Check the resources for this summary and close them foreach (var resourceName in resList) { var resource = (ResourceBase)CreateResource(resourceName); // When we finished, make sure the resource is closed again // Close the resource resource.PersistClose(); // Post recover notification resource.OnPostRecover(); } } }