Example #1
0
 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);
            }
        }
Example #3
0
 public JournalSummary(JournalFile journalFile)
 {
     JournalFile = journalFile;
     Resources   = new List <string>();
 }
Example #4
0
        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();
                }
            }
        }
Example #5
0
        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;
                        }
                    }
                }
            }
        }
Example #6
0
 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();
                }
            }
        }