Exemple #1
0
        /// <summary>
        /// Load all confirmed transactions from log file (used only when open datafile)
        /// Don't need lock because it's called on ctor of LiteEngine
        /// </summary>
        public void RestoreIndex(ref HeaderPage header)
        {
            // get all page positions
            var positions = new Dictionary <long, List <PagePosition> >();
            var current   = 0L;

            // read all pages to get confirmed transactions (do not read page content, only page header)
            foreach (var buffer in _disk.ReadFull(FileOrigin.Log))
            {
                // read direct from buffer to avoid create BasePage structure
                var pageID        = buffer.ReadUInt32(BasePage.P_PAGE_ID);
                var isConfirmed   = buffer.ReadBool(BasePage.P_IS_CONFIRMED);
                var transactionID = buffer.ReadUInt32(BasePage.P_TRANSACTION_ID);

                var position = new PagePosition(pageID, current);

                if (positions.TryGetValue(transactionID, out var list))
                {
                    list.Add(position);
                }
                else
                {
                    positions[transactionID] = new List <PagePosition> {
                        position
                    };
                }

                if (isConfirmed)
                {
                    this.ConfirmTransaction(transactionID, positions[transactionID]);

                    var pageType = (PageType)buffer.ReadByte(BasePage.P_PAGE_TYPE);

                    // when a header is modified in transaction, must always be the last page inside log file (per transaction)
                    if (pageType == PageType.Header)
                    {
                        // page buffer instance can't change
                        var headerBuffer = header.Buffer;

                        // copy this buffer block into original header block
                        Buffer.BlockCopy(buffer.Array, buffer.Offset, headerBuffer.Array, headerBuffer.Offset, PAGE_SIZE);

                        // re-load header (using new buffer data)
                        header = new HeaderPage(headerBuffer);
                        header.TransactionID = uint.MaxValue;
                        header.IsConfirmed   = false;
                    }
                }

                // update last transaction ID
                _lastTransactionID = (int)transactionID;

                current += PAGE_SIZE;
            }
        }
        /// <summary>
        /// Return added pages when occurs an rollback transaction (run this only in rollback). Create new transactionID and add into
        /// Log file all new pages as EmptyPage in a linked order - also, update SharedPage before store
        /// </summary>
        private void ReturnNewPages()
        {
            // create new transaction ID
            var transactionID = _walIndex.NextTransactionID();

            // now lock header to update LastTransactionID/FreePageList
            lock (_header)
            {
                // persist all empty pages into wal-file
                var pagePositions = new Dictionary <uint, PagePosition>();

                IEnumerable <PageBuffer> source()
                {
                    // create list of empty pages with forward link pointer
                    for (var i = 0; i < _transPages.NewPages.Count; i++)
                    {
                        var pageID = _transPages.NewPages[i];
                        var next   = i < _transPages.NewPages.Count - 1 ? _transPages.NewPages[i + 1] : _header.FreeEmptyPageID;

                        var buffer = _disk.NewPage();

                        var page = new BasePage(buffer, pageID, PageType.Empty)
                        {
                            NextPageID    = next,
                            TransactionID = transactionID
                        };

                        yield return(page.UpdateBuffer());

                        // update wal
                        pagePositions[pageID] = new PagePosition(pageID, buffer.Position);

                        if (_disposed)
                        {
                            yield break;
                        }
                    }

                    // update header page with my new transaction ID
                    _header.TransactionID   = transactionID;
                    _header.FreeEmptyPageID = _transPages.NewPages[0];
                    _header.IsConfirmed     = true;

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

                    Buffer.BlockCopy(buf.Array, buf.Offset, clone.Array, clone.Offset, clone.Count);

                    yield return(clone);
                };

                // create a header save point before any change
                var safepoint = _header.Savepoint();

                try
                {
                    // write all pages (including new header)
                    _disk.WriteAsync(source());
                }
                catch
                {
                    // must revert all header content if any error occurs during header change
                    _header.Restore(safepoint);
                    throw;
                }

                // now confirm this transaction to wal
                _walIndex.ConfirmTransaction(transactionID, pagePositions.Values);
            }
        }
Exemple #3
0
        /// <summary>
        /// Load all confirmed transactions from log file (used only when open datafile)
        /// Don't need lock because it's called on ctor of LiteEngine
        /// Update _disk instance with last log position
        /// </summary>
        public void RestoreIndex(HeaderPage header)
        {
            // get all page positions
            var positions = new Dictionary <long, List <PagePosition> >();
            var last      = 0L;

            // read all pages to get confirmed transactions (do not read page content, only page header)
            foreach (var buffer in _disk.ReadLog(true))
            {
                // read direct from buffer to avoid create BasePage structure
                var pageID        = buffer.ReadUInt32(BasePage.P_PAGE_ID);
                var isConfirmed   = buffer.ReadBool(BasePage.P_IS_CONFIRMED);
                var transactionID = buffer.ReadUInt32(BasePage.P_TRANSACTION_ID);

                if (transactionID == 0 || transactionID == uint.MaxValue)
                {
                    continue;
                }

                var position = new PagePosition(pageID, buffer.Position);

                if (positions.TryGetValue(transactionID, out var list))
                {
                    list.Add(position);
                }
                else
                {
                    positions[transactionID] = new List <PagePosition> {
                        position
                    };
                }

                if (isConfirmed)
                {
                    // update last IsConfirmed page
                    last = buffer.Position;

                    this.ConfirmTransaction(transactionID, positions[transactionID]);

                    var pageType = (PageType)buffer.ReadByte(BasePage.P_PAGE_TYPE);

                    // when a header is modified in transaction, must always be the last page inside log file (per transaction)
                    if (pageType == PageType.Header)
                    {
                        // copy this buffer block into original header block
                        Buffer.BlockCopy(buffer.Array, buffer.Offset, header.Buffer.Array, header.Buffer.Offset, PAGE_SIZE);

                        header.LoadPage();
                    }
                }

                // update last transaction ID
                _lastTransactionID = (int)transactionID;
            }

            // update last log position acording with last IsConfirmed on log
            if (last > 0)
            {
                _disk.LogEndPosition = last + PAGE_SIZE;
            }
        }