/// <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); } }
/// <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; } }