private IEnumerable <BsonDocument> GetList(uint pageID, string indexName, TransactionService transaction, Snapshot snapshot) { if (pageID == uint.MaxValue) { yield break; } var page = snapshot.GetPage <BasePage>(pageID); while (page != null) { _collections.TryGetValue(page.ColID, out var collection); yield return(new BsonDocument { ["pageID"] = (int)page.PageID, ["pageType"] = page.PageType.ToString(), ["slot"] = (int)page.PageListSlot, ["collection"] = collection, ["index"] = indexName, ["freeBytes"] = page.FreeBytes, ["itemsCount"] = (int)page.ItemsCount }); if (page.NextPageID == uint.MaxValue) { break; } transaction.Safepoint(); page = snapshot.GetPage <BasePage>(page.NextPageID); } }
/// <summary> /// Get last _id index key from collection. Returns MinValue if collection are empty /// </summary> private async Task <BsonValue> GetLastId(Snapshot snapshot) { var pk = snapshot.CollectionPage.PK; // get tail page and previous page var tailPage = await snapshot.GetPage <IndexPage>(pk.Tail.PageID); var node = tailPage.GetIndexNode(pk.Tail.Index); var prevNode = node.Prev[0]; if (prevNode == pk.Head) { return(BsonValue.MinValue); } else { var lastPage = prevNode.PageID == tailPage.PageID ? tailPage : await snapshot.GetPage <IndexPage>(prevNode.PageID); var lastNode = lastPage.GetIndexNode(prevNode.Index); var lastKey = lastNode.Key; return(lastKey); } }
/// <summary> /// Get a node inside a page using PageAddress - Returns null if address IsEmpty /// </summary> public IndexNode GetNode(PageAddress address) { if (address.PageID == uint.MaxValue) { return(null); } var indexPage = _snapshot.GetPage <IndexPage>(address.PageID); return(indexPage.GetIndexNode(address.Index)); }
/// <summary> /// Get collection page instance (or create a new one) /// </summary> public void Get(string name, bool addIfNotExists, ref CollectionPage collectionPage) { // get collection pageID from header var pageID = _header.GetCollectionPageID(name); if (pageID != uint.MaxValue) { collectionPage = _snapshot.GetPage <CollectionPage>(pageID); } else if (addIfNotExists) { this.Add(name, ref collectionPage); } }
/// <summary> /// Get collection page instance (or create a new one). Returns null if not found and not created /// </summary> public async Task <CollectionPage> Get(string name, bool addIfNotExists) { // get collection pageID from header var pageID = _header.GetCollectionPageID(name); if (pageID != uint.MaxValue) { return(await _snapshot.GetPage <CollectionPage>(pageID)); } else if (addIfNotExists) { return(await this.Add(name)); } return(null); }
/// <summary> /// Update document using same page position as reference /// </summary> public void Update(CollectionPage col, PageAddress blockAddress, BsonDocument doc) { var bytesLeft = doc.GetBytesCount(true); if (bytesLeft > MAX_DOCUMENT_SIZE) { throw new LiteException(0, "Document size exceed {0} limit", MAX_DOCUMENT_SIZE); } DataBlock lastBlock = null; var updateAddress = blockAddress; IEnumerable <BufferSlice> source() { var bytesToCopy = 0; while (bytesLeft > 0) { // if last block contains new block sequence, continue updating if (updateAddress.IsEmpty == false) { var dataPage = _snapshot.GetPage <DataPage>(updateAddress.PageID); var currentBlock = dataPage.GetBlock(updateAddress.Index); // try get full page size content bytesToCopy = Math.Min(bytesLeft, dataPage.FreeBytes + currentBlock.Buffer.Count); // get current free slot linked list var slot = BasePage.FreeIndexSlot(dataPage.FreeBytes); var updateBlock = dataPage.UpdateBlock(currentBlock, bytesToCopy); _snapshot.AddOrRemoveFreeList(dataPage, slot); yield return(updateBlock.Buffer); lastBlock = updateBlock; // go to next address (if extits) updateAddress = updateBlock.NextBlock; } else { bytesToCopy = Math.Min(bytesLeft, MAX_DATA_BYTES_PER_PAGE); var dataPage = _snapshot.GetFreePage <DataPage>(bytesToCopy + DataBlock.DATA_BLOCK_FIXED_SIZE); var insertBlock = dataPage.InsertBlock(bytesToCopy, true); if (lastBlock != null) { lastBlock.SetNextBlock(insertBlock.Position); } yield return(insertBlock.Buffer); lastBlock = insertBlock; } bytesLeft -= bytesToCopy; } // old document was bigger than current, must delete extend blocks if (lastBlock.NextBlock.IsEmpty == false) { this.Delete(lastBlock.NextBlock); } } // consume all source bytes to write BsonDocument direct into PageBuffer // must be fastest as possible using (var w = new BufferWriter(source())) { // already bytes count calculate at method start w.WriteDocument(doc, false); w.Consume(); } }
/// <summary> /// Update document using same page position as reference /// </summary> public async Task Update(CollectionPage col, PageAddress blockAddress, BsonDocument doc) { var bytesLeft = doc.GetBytesCount(true); if (bytesLeft > MAX_DOCUMENT_SIZE) { throw new LiteException(0, "Document size exceed {0} limit", MAX_DOCUMENT_SIZE); } DataBlock lastBlock = null; var updateAddress = blockAddress; async IAsyncEnumerable <BufferSlice> source() { var bytesToCopy = 0; while (bytesLeft > 0) { // if last block contains new block sequence, continue updating if (updateAddress.IsEmpty == false) { var dataPage = await _snapshot.GetPage <DataPage>(updateAddress.PageID); var currentBlock = dataPage.GetBlock(updateAddress.Index); // try get full page size content (do not add DATA_BLOCK_FIXED_SIZE because will be added in UpdateBlock) bytesToCopy = Math.Min(bytesLeft, dataPage.FreeBytes + currentBlock.Buffer.Count); var updateBlock = dataPage.UpdateBlock(currentBlock, bytesToCopy); await _snapshot.AddOrRemoveFreeDataList(dataPage); yield return(updateBlock.Buffer); lastBlock = updateBlock; // go to next address (if exists) updateAddress = updateBlock.NextBlock; } else { bytesToCopy = Math.Min(bytesLeft, MAX_DATA_BYTES_PER_PAGE); var dataPage = await _snapshot.GetFreeDataPage(bytesToCopy + DataBlock.DATA_BLOCK_FIXED_SIZE); var insertBlock = dataPage.InsertBlock(bytesToCopy, true); if (lastBlock != null) { lastBlock.SetNextBlock(insertBlock.Position); } await _snapshot.AddOrRemoveFreeDataList(dataPage); yield return(insertBlock.Buffer); lastBlock = insertBlock; } bytesLeft -= bytesToCopy; } // old document was bigger than current, must delete extend blocks if (lastBlock.NextBlock.IsEmpty == false) { var nextBlockAddress = lastBlock.NextBlock; lastBlock.SetNextBlock(PageAddress.Empty); await this.Delete(nextBlockAddress); } } // consume all source bytes to write BsonDocument direct into PageBuffer await using (var writer = await BufferWriterAsync.CreateAsync(source())) { // already bytes count calculate at method start await writer.WriteDocument(doc, false); await writer.Consume(); } }