Exemplo n.º 1
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);
        }