/// <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> /// Initialize LiteEngine using initial engine settings /// </summary> public LiteEngine(EngineSettings settings) { _settings = settings ?? throw new ArgumentNullException(nameof(settings)); // clear checkpoint if database is readonly if (_settings.ReadOnly) { _settings.Checkpoint = 0; } LOG($"start initializing{(_settings.ReadOnly ? " (readonly)" : "")}", "ENGINE"); try { // initialize locker service (no dependency) _locker = new LockService(settings.Timeout, settings.ReadOnly); // initialize disk service (will create database if needed) _disk = new DiskService(settings); // read page with no cache ref (has a own PageBuffer) - do not Release() support var buffer = _disk.ReadFull(FileOrigin.Data).First(); // if first byte are 1 this datafile are encrypted but has do defined password to open if (buffer[0] == 1) { throw new LiteException(0, "This data file is encrypted and needs a password to open"); } _header = new HeaderPage(buffer); // initialize wal-index service _walIndex = new WalIndexService(_disk, _locker); // if exists log file, restore wal index references (can update full _header instance) if (_disk.GetLength(FileOrigin.Log) > 0) { _walIndex.RestoreIndex(ref _header); } // initialize sort temp disk _sortDisk = new SortDisk(settings.CreateTempFactory(), CONTAINER_SORT_SIZE, settings.UtcDate); // initialize transaction monitor as last service _monitor = new TransactionMonitor(_header, _locker, _disk, _walIndex, _settings); // register system collections this.InitializeSystemCollections(); LOG("initialization completed", "ENGINE"); } catch (Exception ex) { LOG(ex.Message, "ERROR"); // explicit dispose (but do not run shutdown operation) this.Dispose(true); throw; } }
/// <summary> /// Get all pages from datafile based only in Page Position (get from disk - not from cache) /// </summary> private IEnumerable <BsonDocument> DumpPages() { var collections = _header.GetCollections().ToDictionary(x => x.Value, x => x.Key); foreach (var buffer in _disk.ReadFull()) { var page = new BasePage(buffer); var doc = new BsonDocument { ["position"] = Number(buffer.Position), ["origin"] = page.PageID == 0 && page.TransactionID == 0 && page.PageType == PageType.Empty ? "blank" : buffer.Position < _disk.LogStartPosition ? "data" : "log", ["pageID"] = (int)page.PageID, ["pageType"] = page.PageType.ToString(), ["nextPageID"] = (int)page.NextPageID, ["prevPageID"] = (int)page.PrevPageID, ["collection"] = collections.GetOrDefault(page.ColID, "-"), ["transactionID"] = (int)page.TransactionID, ["isConfirmed"] = page.IsConfirmed, ["itemsCount"] = (int)page.ItemsCount, ["freeBytes"] = page.FreeBytes, ["usedBytes"] = (int)page.UsedBytes, ["fragmentedBytes"] = (int)page.FragmentedBytes, ["nextFreePosition"] = (int)page.NextFreePosition, ["highestIndex"] = (int)page.HighestIndex }; yield return(doc); } }