Пример #1
0
        /// <summary>
        /// Persist all dirty in-memory pages (in all snapshots) and clear local pages list (even clean pages)
        /// </summary>
        private int PersistDirtyPages(bool commit)
        {
            var dirty = 0;

            // inner method to get all dirty pages
            IEnumerable <PageBuffer> source()
            {
                // get all dirty pages from all write snapshots
                // can include (or not) collection pages
                // update DirtyPagesLog inside transPage for all dirty pages was write on disk
                var pages = _snapshots.Values
                            .Where(x => x.Mode == LockMode.Write)
                            .SelectMany(x => x.GetWritablePages(true, commit));

                // mark last dirty page as confirmed only if there is no header change in commit
                var markLastAsConfirmed = commit && _transPages.HeaderChanged == false;

                // loop across all dirty pages
                if (markLastAsConfirmed)
                {
                    // neet use "IsLast" method to get when loop are last item
                    foreach (var page in pages.IsLast())
                    {
                        // update page transactionID
                        page.Item.TransactionID = _transactionID;

                        // if last page, mask as confirm
                        if (page.IsLast)
                        {
                            page.Item.IsConfirmed = true;
                        }

                        var buffer = page.Item.UpdateBuffer();

                        // buffer position will be set at end of file (it´s always log file)
                        yield return(buffer);

                        dirty++;

                        _transPages.DirtyPages[page.Item.PageID] = new PagePosition(page.Item.PageID, buffer.Position);
                    }
                }
                else
                {
                    // avoid use "IsLast" when was not needed (better performance)
                    foreach (var page in pages)
                    {
                        // update page transactionID
                        page.TransactionID = _transactionID;

                        var buffer = page.UpdateBuffer();

                        // buffer position will be set at end of file (it´s always log file)
                        yield return(buffer);

                        dirty++;

                        _transPages.DirtyPages[page.PageID] = new PagePosition(page.PageID, buffer.Position);
                    }
                }

                // in commit with header page change, last page will be header
                if (commit && _transPages.HeaderChanged)
                {
                    // lock header page to avoid concurrency when writing on header
                    lock (_header)
                    {
                        var newEmptyPageID = _header.FreeEmptyPageID;

                        // if has deleted pages in this transaction, fix FreeEmptyPageID
                        if (_transPages.DeletedPages > 0)
                        {
                            // now, my free list will starts with first page ID
                            newEmptyPageID = _transPages.FirstDeletedPageID;

                            // if free empty list was not empty, let's fix my last page
                            if (_header.FreeEmptyPageID != uint.MaxValue)
                            {
                                var empty = _disk.NewPage();

                                // to avoid read a empty page from disk I will create new page as empty and override it
                                var lastDeletedPage = new BasePage(empty, _transPages.LastDeletedPageID, PageType.Empty)
                                {
                                    // update nextPageID of last deleted page to old first page ID
                                    NextPageID    = _header.FreeEmptyPageID,
                                    TransactionID = _transactionID
                                };

                                // this page will write twice on wal, but no problem, only this last version will be saved on data file
                                yield return(lastDeletedPage.UpdateBuffer());
                            }
                        }

                        // update this confirm page with current transactionID
                        _header.FreeEmptyPageID = newEmptyPageID;
                        _header.TransactionID   = _transactionID;

                        // this header page will be marked as confirmed page in log file
                        _header.IsConfirmed = true;

                        // invoke all header callbacks (new/drop collections)
                        _transPages.OnCommit(_header);

                        // clone header page
                        var buffer = _header.UpdateBuffer();
                        var clone  = _disk.NewPage();

                        // mem copy from current header to new header clone
                        Buffer.BlockCopy(buffer.Array, buffer.Offset, clone.Array, clone.Offset, clone.Count);

                        // persist header in log file
                        yield return(clone);
                    }
                }
            };

            // write all dirty pages, in sequence on log-file and store references into log pages on transPages
            // (works only for Write snapshots)
            var count = _disk.WriteAsync(source());

            // now, discard all clean pages (because those pages are writable and must be readable)
            // from write snapshots
            _disk.DiscardPages(_snapshots.Values
                               .Where(x => x.Mode == LockMode.Write)
                               .SelectMany(x => x.GetWritablePages(false, commit))
                               .Select(x => x.Buffer), false);

            return(count);
        }
Пример #2
0
        /// <summary>
        /// Persist all dirty in-memory pages (in all snapshots) and clear local pages list (even clean pages)
        /// </summary>
        private async Task <int> PersistDirtyPages(bool commit)
        {
            var dirty = 0;

            // inner method to get all dirty pages
            IEnumerable <PageBuffer> source()
            {
                // get all dirty pages from all write snapshots
                // can include (or not) collection pages
                // update DirtyPagesLog inside transPage for all dirty pages was write on disk
                var pages = _snapshots.Values
                            .Where(x => x.Mode == LockMode.Write)
                            .SelectMany(x => x.GetWritablePages(true, commit));

                // mark last dirty page as confirmed only if there is no header change in commit
                var markLastAsConfirmed = commit && _transPages.HeaderChanged == false;

                // neet use "IsLast" method to get when loop are last item
                foreach (var page in pages.IsLast())
                {
                    // update page transactionID
                    page.Item.TransactionID = _transactionID;

                    // if last page, mask as confirm (only if a real commit and no header changes)
                    if (page.IsLast)
                    {
                        page.Item.IsConfirmed = markLastAsConfirmed;
                    }

                    // if current page is last deleted page, point this page to last free
                    if (_transPages.LastDeletedPageID == page.Item.PageID && commit)
                    {
                        ENSURE(_transPages.HeaderChanged, "must header be in lock");
                        ENSURE(page.Item.PageType == PageType.Empty, "must be marked as deleted page");

                        // join existing free list pages into new list of deleted pages
                        page.Item.NextPageID = _header.FreeEmptyPageList;

                        // and now, set header free list page to this new list
                        _header.FreeEmptyPageList = _transPages.FirstDeletedPageID;
                    }

                    var buffer = page.Item.UpdateBuffer();

                    // buffer position will be set at end of file (it´s always log file)
                    yield return(buffer);

                    dirty++;

                    _transPages.DirtyPages[page.Item.PageID] = new PagePosition(page.Item.PageID, buffer.Position);
                }

                // in commit with header page change, last page will be header
                if (commit && _transPages.HeaderChanged)
                {
                    // update this confirm page with current transactionID
                    _header.TransactionID = _transactionID;

                    // this header page will be marked as confirmed page in log file
                    _header.IsConfirmed = true;

                    // invoke all header callbacks (new/drop collections)
                    _transPages.OnCommit(_header);

                    // clone header page
                    var buffer = _header.UpdateBuffer();
                    var clone  = _disk.Cache.NewPage();

                    // mem copy from current header to new header clone
                    Buffer.BlockCopy(buffer.Array, buffer.Offset, clone.Array, clone.Offset, clone.Count);

                    // persist header in log file
                    yield return(clone);
                }
            };

            // write all dirty pages, in sequence on log-file and store references into log pages on transPages
            // (works only for Write snapshots)
            var count = await _disk.WriteLogPages(source());

            // now, discard all clean pages (because those pages are writable and must be readable)
            // from write snapshots
            _disk.Cache.DiscardCleanPages(_snapshots.Values
                                          .Where(x => x.Mode == LockMode.Write)
                                          .SelectMany(x => x.GetWritablePages(false, commit))
                                          .Select(x => x.Buffer));

            return(count);
        }