/// <summary>
 /// Set a page as dirty and ensure page are in cache. Should be used after any change on page
 /// Do not use on end of method because page can be deleted/change type
 /// Always remove from clean list and add in dirty list
 /// [ThreadSafe]
 /// </summary>
 public void SetDirty(BasePage page)
 {
     _clean.Remove(page.PageID);
     page.IsDirty        = true;
     _dirty[page.PageID] = page;
 }
        /// <summary>
        /// Save all dirty pages to disk
        /// </summary>
        public void PersistDirtyPages()
        {
            // get header page
            var header = _pager.GetPage <HeaderPage>(0);

            // increase file changeID (back to 0 when overflow)
            header.ChangeID = header.ChangeID == ushort.MaxValue ? (ushort)0 : (ushort)(header.ChangeID + (ushort)1);

            // mark header as dirty
            _pager.SetDirty(header);

            _log.Write(Logger.DISK, "begin disk operations - changeID: {0}", header.ChangeID);

            // write journal file in desc order to header be last page in disk
            if (_disk.IsJournalEnabled)
            {
                _disk.WriteJournal(_cache.GetDirtyPages()
                                   .OrderByDescending(x => x.PageID)
                                   .Select(x => x.DiskData)
                                   .Where(x => x.Length > 0)
                                   .ToList(), header.LastPageID);

                // mark header as recovery before start writing (in journal, must keep recovery = false)
                header.Recovery = true;

                // flush to disk to ensure journal is committed to disk before proceeding
                _disk.Flush();
            }
            else
            {
                // if no journal extend, resize file here to fast writes
                _disk.SetLength(BasePage.GetSizeOfPages(header.LastPageID + 1));
            }

            // write header page first. if header.Recovery == true, this ensures it's written to disk *before* we start changing pages
            var headerPage   = _cache.GetPage(0);
            var headerBuffer = headerPage.WritePage();

            _disk.WritePage(0, headerBuffer);
            _disk.Flush();

            // get all dirty page stating from Header page (SortedList)
            // header page (id=0) always must be first page to write on disk because it's will mark disk as "in recovery"
            foreach (var page in _cache.GetDirtyPages())
            {
                // we've already written the header, so skip it
                if (page.PageID == 0)
                {
                    continue;
                }

                // page.WritePage() updated DiskData with new rendered buffer
                var buffer = _crypto == null || page.PageID == 0 ?
                             page.WritePage() :
                             _crypto.Encrypt(page.WritePage());

                _disk.WritePage(page.PageID, buffer);
            }

            if (_disk.IsJournalEnabled)
            {
                // ensure changed pages are persisted to disk *before* we change header.Recovery to false
                _disk.Flush();

                // re-write header page but now with recovery=false
                header.Recovery = false;

                _log.Write(Logger.DISK, "re-write header page now with recovery = false");

                _disk.WritePage(0, header.WritePage());
            }

            // mark all dirty pages as clean pages (all are persisted in disk and are valid pages)
            _cache.MarkDirtyAsClean();

            // flush all data direct to disk
            _disk.Flush();

            // discard journal file
            _disk.ClearJournal(header.LastPageID);
        }
示例#3
0
        /// <summary>
        /// Shrink datafile to crop journal area
        /// </summary>
        public void ClearJournal(uint lastPageID)
        {
            _log.Write(Logger.JOURNAL, "shrink datafile to remove journal area");

            this.SetLength(BasePage.GetSizeOfPages(lastPageID + 1));
        }