const std::string TileGroupHeader::GetInfo() const { std::ostringstream os; os << "\t-----------------------------------------------------------\n"; os << "\tTILE GROUP HEADER \n"; oid_t active_tuple_slots = GetCurrentNextTupleSlot(); peloton::ItemPointer item; for (oid_t header_itr = 0; header_itr < active_tuple_slots; header_itr++) { txn_id_t txn_id = GetTransactionId(header_itr); cid_t beg_commit_id = GetBeginCommitId(header_itr); cid_t end_commit_id = GetEndCommitId(header_itr); bool insert_commit = GetInsertCommit(header_itr); bool delete_commit = GetDeleteCommit(header_itr); int width = 10; os << "\t txn id : "; if (txn_id == MAX_TXN_ID) os << std::setw(width) << "MAX_TXN_ID"; else os << std::setw(width) << txn_id; os << " beg cid : "; if (beg_commit_id == MAX_CID) os << std::setw(width) << "MAX_CID"; else os << std::setw(width) << beg_commit_id; os << " end cid : "; if (end_commit_id == MAX_CID) os << std::setw(width) << "MAX_CID"; else os << std::setw(width) << end_commit_id; os << " insert commit : "; if (insert_commit == true) os << "O"; else os << "X"; os << " delete commit : "; if (delete_commit == true) os << "O"; else os << "X"; peloton::ItemPointer location = GetNextItemPointer(header_itr); peloton::ItemPointer location2 = GetPrevItemPointer(header_itr); os << " next : " << "[ " << location.block << " , " << location.offset << " ]\n" << " prev : " << "[ " << location2.block << " , " << location2.offset << " ]\n"; } os << "\t-----------------------------------------------------------\n"; return os.str(); }
void TileGroupHeader::PrintVisibility(txn_id_t txn_id, cid_t at_cid) { oid_t active_tuple_slots = GetCurrentNextTupleSlot(); std::stringstream os; os << "\t-----------------------------------------------------------\n"; for (oid_t header_itr = 0; header_itr < active_tuple_slots; header_itr++) { bool own = (txn_id == GetTransactionId(header_itr)); bool activated = (at_cid >= GetBeginCommitId(header_itr)); bool invalidated = (at_cid >= GetEndCommitId(header_itr)); txn_id_t txn_id = GetTransactionId(header_itr); cid_t beg_commit_id = GetBeginCommitId(header_itr); cid_t end_commit_id = GetEndCommitId(header_itr); int width = 10; os << "\tslot :: " << std::setw(width) << header_itr; os << " txn id : "; if (txn_id == MAX_TXN_ID) os << std::setw(width) << "MAX_TXN_ID"; else os << std::setw(width) << txn_id; os << " beg cid : "; if (beg_commit_id == MAX_CID) os << std::setw(width) << "MAX_CID"; else os << std::setw(width) << beg_commit_id; os << " end cid : "; if (end_commit_id == MAX_CID) os << std::setw(width) << "MAX_CID"; else os << std::setw(width) << end_commit_id; peloton::ItemPointer location = GetNextItemPointer(header_itr); os << " prev : " << "[ " << location.block << " , " << location.offset << " ]"; //<< os << " own : " << own; os << " activated : " << activated; os << " invalidated : " << invalidated << " "; // Visible iff past Insert || Own Insert if ((!own && activated && !invalidated) || (own && !activated && !invalidated)) os << "\t\t[ true ]\n"; else os << "\t\t[ false ]\n"; } os << "\t-----------------------------------------------------------\n"; LOG_TRACE("%s", os.str().c_str()); }
// Validate that MVCC storage is correct, it assumes an old-to-new chain // Invariants // 1. Transaction id should either be INVALID_TXNID or INITIAL_TXNID // 2. Begin commit id should <= end commit id // 3. Timestamp consistence // 4. Version doubly linked list consistency static void ValidateMVCC_OldToNew(storage::DataTable *table) { auto &catalog_manager = catalog::Manager::GetInstance(); LOG_INFO("Validating MVCC storage"); int tile_group_count = table->GetTileGroupCount(); LOG_INFO("The table has %d tile groups in the table", tile_group_count); for (int tile_group_offset = 0; tile_group_offset < tile_group_count; tile_group_offset++) { LOG_INFO("Validate tile group #%d", tile_group_offset); auto tile_group = table->GetTileGroup(tile_group_offset); auto tile_group_header = tile_group->GetHeader(); size_t tuple_count = tile_group->GetAllocatedTupleCount(); LOG_INFO("Tile group #%d has allocated %lu tuples", tile_group_offset, tuple_count); // 1. Transaction id should either be INVALID_TXNID or INITIAL_TXNID for (oid_t tuple_slot = 0; tuple_slot < tuple_count; tuple_slot++) { txn_id_t txn_id = tile_group_header->GetTransactionId(tuple_slot); EXPECT_TRUE(txn_id == INVALID_TXN_ID || txn_id == INITIAL_TXN_ID) << "Transaction id is not INVALID_TXNID or INITIAL_TXNID"; } LOG_INFO("[OK] All tuples have valid txn id"); // double avg_version_chain_length = 0.0; for (oid_t tuple_slot = 0; tuple_slot < tuple_count; tuple_slot++) { txn_id_t txn_id = tile_group_header->GetTransactionId(tuple_slot); cid_t begin_cid = tile_group_header->GetBeginCommitId(tuple_slot); cid_t end_cid = tile_group_header->GetEndCommitId(tuple_slot); ItemPointer next_location = tile_group_header->GetNextItemPointer(tuple_slot); ItemPointer prev_location = tile_group_header->GetPrevItemPointer(tuple_slot); // 2. Begin commit id should <= end commit id EXPECT_TRUE(begin_cid <= end_cid) << "Tuple begin commit id is less than or equal to end commit id"; // This test assumes a oldest-to-newest version chain if (txn_id != INVALID_TXN_ID) { EXPECT_TRUE(begin_cid != MAX_CID) << "Non invalid txn shouldn't have a MAX_CID begin commit id"; // The version is an oldest version if (prev_location.IsNull()) { if (next_location.IsNull()) { EXPECT_EQ(end_cid, MAX_CID) << "Single version has a non MAX_CID end commit time"; } else { cid_t prev_end_cid = end_cid; ItemPointer prev_location(tile_group->GetTileGroupId(), tuple_slot); while (!next_location.IsNull()) { auto next_tile_group = catalog_manager.GetTileGroup(next_location.block); auto next_tile_group_header = next_tile_group->GetHeader(); txn_id_t next_txn_id = next_tile_group_header->GetTransactionId( next_location.offset); if (next_txn_id == INVALID_TXN_ID) { // If a version in the version chain has a INVALID_TXN_ID, it // must be at the tail // of the chain. It is either because we have deleted a tuple // (so append a invalid tuple), // or because this new version is aborted. EXPECT_TRUE( next_tile_group_header->GetNextItemPointer( next_location.offset).IsNull()) << "Invalid version in a version chain and is not delete"; } cid_t next_begin_cid = next_tile_group_header->GetBeginCommitId( next_location.offset); cid_t next_end_cid = next_tile_group_header->GetEndCommitId(next_location.offset); // 3. Timestamp consistence if (next_begin_cid == MAX_CID) { // It must be an aborted version, it should be at the end of the // chain EXPECT_TRUE( next_tile_group_header->GetNextItemPointer( next_location.offset).IsNull()) << "Version with MAX_CID begin cid is not version tail"; } else { EXPECT_EQ(prev_end_cid, next_begin_cid) << "Prev end commit id should equal net begin commit id"; ItemPointer next_prev_location = next_tile_group_header->GetPrevItemPointer( next_location.offset); // 4. Version doubly linked list consistency EXPECT_TRUE(next_prev_location.offset == prev_location.offset && next_prev_location.block == prev_location.block) << "Next version's prev version does not match"; } prev_location = next_location; prev_end_cid = next_end_cid; next_location = next_tile_group_header->GetNextItemPointer( next_location.offset); } // Now prev_location is at the tail of the version chain ItemPointer last_location = prev_location; auto last_tile_group = catalog_manager.GetTileGroup(last_location.block); auto last_tile_group_header = last_tile_group->GetHeader(); // txn_id_t last_txn_id = // last_tile_group_header->GetTransactionId(last_location.offset); cid_t last_end_cid = last_tile_group_header->GetEndCommitId(last_location.offset); EXPECT_TRUE( last_tile_group_header->GetNextItemPointer(last_location.offset) .IsNull()) << "Last version has a next pointer"; EXPECT_EQ(last_end_cid, MAX_CID) << "Last version doesn't end with MAX_CID"; } } } else { EXPECT_TRUE(tile_group_header->GetNextItemPointer(tuple_slot).IsNull()) << "Invalid tuple must not have next item pointer"; } } LOG_INFO("[OK] oldest-to-newest version chain validated"); } }
bool IndexScanExecutor::ExecPrimaryIndexLookup() { PL_ASSERT(!done_); std::vector<ItemPointer *> tuple_location_ptrs; PL_ASSERT(index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY); if (0 == key_column_ids_.size()) { index_->ScanAllKeys(tuple_location_ptrs); } else { index_->Scan(values_, key_column_ids_, expr_types_, SCAN_DIRECTION_TYPE_FORWARD, tuple_location_ptrs); } if (tuple_location_ptrs.size() == 0) return false; auto &transaction_manager = concurrency::TransactionManagerFactory::GetInstance(); std::map<oid_t, std::vector<oid_t>> visible_tuples; std::vector<ItemPointer> garbage_tuples; // for every tuple that is found in the index. for (auto tuple_location_ptr : tuple_location_ptrs) { ItemPointer tuple_location = *tuple_location_ptr; auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuple_location.block); auto tile_group_header = tile_group.get()->GetHeader(); size_t chain_length = 0; while (true) { ++chain_length; // if the tuple is visible. if (transaction_manager.IsVisible(tile_group_header, tuple_location.offset)) { LOG_TRACE("traverse chain length : %lu", chain_length); LOG_TRACE("perform read: %u, %u", tuple_location.block, tuple_location.offset); // perform predicate evaluation. if (predicate_ == nullptr) { visible_tuples[tuple_location.block].push_back(tuple_location.offset); auto res = transaction_manager.PerformRead(tuple_location); if (!res) { transaction_manager.SetTransactionResult(RESULT_FAILURE); return res; } } else { expression::ContainerTuple<storage::TileGroup> tuple( tile_group.get(), tuple_location.offset); auto eval = predicate_->Evaluate(&tuple, nullptr, executor_context_).IsTrue(); if (eval == true) { visible_tuples[tuple_location.block] .push_back(tuple_location.offset); auto res = transaction_manager.PerformRead(tuple_location); if (!res) { transaction_manager.SetTransactionResult(RESULT_FAILURE); return res; } } } break; } // if the tuple is not visible. else { ItemPointer old_item = tuple_location; cid_t old_end_cid = tile_group_header->GetEndCommitId(old_item.offset); tuple_location = tile_group_header->GetNextItemPointer(old_item.offset); // there must exist a visible version. // FIXME: currently, only speculative read transaction manager **may** see a null version // it's a potential bug if(tuple_location.IsNull()) { transaction_manager.SetTransactionResult(RESULT_FAILURE); // FIXME: this cause unnecessary abort when we have delete operations return false; } // FIXME: Is this always true? what if we have a deleted tuple? --jiexi PL_ASSERT(tuple_location.IsNull() == false); cid_t max_committed_cid = transaction_manager.GetMaxCommittedCid(); // check whether older version is garbage. if (old_end_cid <= max_committed_cid) { PL_ASSERT(tile_group_header->GetTransactionId(old_item.offset) == INITIAL_TXN_ID || tile_group_header->GetTransactionId(old_item.offset) == INVALID_TXN_ID); if (tile_group_header->SetAtomicTransactionId(old_item.offset, INVALID_TXN_ID) == true) { // atomically swap item pointer held in the index bucket. AtomicUpdateItemPointer(tuple_location_ptr, tuple_location); // currently, let's assume only primary index exists. // gc::GCManagerFactory::GetInstance().RecycleTupleSlot( // table_->GetOid(), old_item.block, old_item.offset, // transaction_manager.GetNextCommitId()); garbage_tuples.push_back(old_item); tile_group = manager.GetTileGroup(tuple_location.block); tile_group_header = tile_group.get()->GetHeader(); tile_group_header->SetPrevItemPointer(tuple_location.offset, INVALID_ITEMPOINTER); } else { tile_group = manager.GetTileGroup(tuple_location.block); tile_group_header = tile_group.get()->GetHeader(); } } else { tile_group = manager.GetTileGroup(tuple_location.block); tile_group_header = tile_group.get()->GetHeader(); } } } } // Add all garbage tuples to GC manager if(garbage_tuples.size() != 0) { cid_t garbage_timestamp = transaction_manager.GetNextCommitId(); for (auto garbage : garbage_tuples) { gc::GCManagerFactory::GetInstance().RecycleTupleSlot( table_->GetOid(), garbage.block, garbage.offset, garbage_timestamp); } } // Construct a logical tile for each block for (auto tuples : visible_tuples) { auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuples.first); std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile()); // Add relevant columns to logical tile logical_tile->AddColumns(tile_group, full_column_ids_); logical_tile->AddPositionList(std::move(tuples.second)); if (column_ids_.size() != 0) { logical_tile->ProjectColumns(full_column_ids_, column_ids_); } result_.push_back(logical_tile.release()); } done_ = true; LOG_TRACE("Result tiles : %lu", result_.size()); return true; }
bool HybridScanExecutor::ExecPrimaryIndexLookup() { assert(!index_done_); std::vector<ItemPointer *> tuple_location_ptrs; assert(index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY); if (0 == key_column_ids_.size()) { index_->ScanAllKeys(tuple_location_ptrs); } else { index_->Scan(values_, key_column_ids_, expr_types_, SCAN_DIRECTION_TYPE_FORWARD, tuple_location_ptrs); } LOG_INFO("Tuple_locations.size(): %lu", tuple_location_ptrs.size()); auto &transaction_manager = concurrency::TransactionManagerFactory::GetInstance(); if (tuple_location_ptrs.size() == 0) { index_done_ = true; return false; } //std::set<oid_t> oid_ts; std::map<oid_t, std::vector<oid_t>> visible_tuples; // for every tuple that is found in the index. for (auto tuple_location_ptr : tuple_location_ptrs) { ItemPointer tuple_location = *tuple_location_ptr; if (type_ == planner::HYBRID && tuple_location.block >= (block_threshold)) { item_pointers_.insert(tuple_location); // oid_ts.insert(tuple_location.block); } auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuple_location.block); auto tile_group_header = tile_group.get()->GetHeader(); // perform transaction read size_t chain_length = 0; while (true) { ++chain_length; if (transaction_manager.IsVisible(tile_group_header, tuple_location.offset)) { visible_tuples[tuple_location.block].push_back(tuple_location.offset); auto res = transaction_manager.PerformRead(tuple_location); if (!res) { transaction_manager.SetTransactionResult(RESULT_FAILURE); return res; } break; } else { ItemPointer old_item = tuple_location; cid_t old_end_cid = tile_group_header->GetEndCommitId(old_item.offset); tuple_location = tile_group_header->GetNextItemPointer(old_item.offset); // there must exist a visible version. assert(tuple_location.IsNull() == false); cid_t max_committed_cid = transaction_manager.GetMaxCommittedCid(); // check whether older version is garbage. if (old_end_cid < max_committed_cid) { assert(tile_group_header->GetTransactionId(old_item.offset) == INITIAL_TXN_ID || tile_group_header->GetTransactionId(old_item.offset) == INVALID_TXN_ID); if (tile_group_header->SetAtomicTransactionId(old_item.offset, INVALID_TXN_ID) == true) { // atomically swap item pointer held in the index bucket. AtomicUpdateItemPointer(tuple_location_ptr, tuple_location); // currently, let's assume only primary index exists. //gc::GCManagerFactory::GetInstance().RecycleTupleSlot( // table_->GetOid(), old_item.block, old_item.offset, // max_committed_cid); } } tile_group = manager.GetTileGroup(tuple_location.block); tile_group_header = tile_group.get()->GetHeader(); } } } // Construct a logical tile for each block for (auto tuples : visible_tuples) { auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuples.first); std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile()); // Add relevant columns to logical tile logical_tile->AddColumns(tile_group, full_column_ids_); logical_tile->AddPositionList(std::move(tuples.second)); if (column_ids_.size() != 0) { logical_tile->ProjectColumns(full_column_ids_, column_ids_); } result_.push_back(logical_tile.release()); } index_done_ = true; LOG_TRACE("Result tiles : %lu", result_.size()); return true; }
Result PessimisticTxnManager::AbortTransaction() { LOG_TRACE("Aborting peloton txn : %lu ", current_txn->GetTransactionId()); auto &manager = catalog::Manager::GetInstance(); auto &rw_set = current_txn->GetRWSet(); for (auto &tile_group_entry : rw_set) { oid_t tile_group_id = tile_group_entry.first; auto tile_group = manager.GetTileGroup(tile_group_id); auto tile_group_header = tile_group->GetHeader(); for (auto &tuple_entry : tile_group_entry.second) { auto tuple_slot = tuple_entry.first; if (tuple_entry.second == RW_TYPE_READ) { if (pessimistic_released_rdlock.find(tile_group_id) == pessimistic_released_rdlock.end() || pessimistic_released_rdlock[tile_group_id].find(tuple_slot) == pessimistic_released_rdlock[tile_group_id].end()) { ReleaseReadLock(tile_group_header, tuple_slot); pessimistic_released_rdlock[tile_group_id].insert(tuple_slot); } } else if (tuple_entry.second == RW_TYPE_UPDATE) { ItemPointer new_version = tile_group_header->GetNextItemPointer(tuple_slot); auto new_tile_group_header = manager.GetTileGroup(new_version.block)->GetHeader(); new_tile_group_header->SetBeginCommitId(new_version.offset, MAX_CID); new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID); COMPILER_MEMORY_FENCE; tile_group_header->SetEndCommitId(tuple_slot, MAX_CID); COMPILER_MEMORY_FENCE; new_tile_group_header->SetTransactionId(new_version.offset, INVALID_TXN_ID); tile_group_header->SetTransactionId(tuple_slot, INITIAL_TXN_ID); } else if (tuple_entry.second == RW_TYPE_DELETE) { ItemPointer new_version = tile_group_header->GetNextItemPointer(tuple_slot); auto new_tile_group_header = manager.GetTileGroup(new_version.block)->GetHeader(); new_tile_group_header->SetBeginCommitId(new_version.offset, MAX_CID); new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID); COMPILER_MEMORY_FENCE; tile_group_header->SetEndCommitId(tuple_slot, MAX_CID); COMPILER_MEMORY_FENCE; new_tile_group_header->SetTransactionId(new_version.offset, INVALID_TXN_ID); tile_group_header->SetTransactionId(tuple_slot, INITIAL_TXN_ID); } else if (tuple_entry.second == RW_TYPE_INSERT) { tile_group_header->SetEndCommitId(tuple_slot, MAX_CID); tile_group_header->SetBeginCommitId(tuple_slot, MAX_CID); COMPILER_MEMORY_FENCE; tile_group_header->SetTransactionId(tuple_slot, INVALID_TXN_ID); } else if (tuple_entry.second == RW_TYPE_INS_DEL) { tile_group_header->SetEndCommitId(tuple_slot, MAX_CID); tile_group_header->SetBeginCommitId(tuple_slot, MAX_CID); COMPILER_MEMORY_FENCE; tile_group_header->SetTransactionId(tuple_slot, INVALID_TXN_ID); } } } EndTransaction(); pessimistic_released_rdlock.clear(); return Result::RESULT_ABORTED; }
Result PessimisticTxnManager::CommitTransaction() { LOG_TRACE("Committing peloton txn : %lu ", current_txn->GetTransactionId()); auto &manager = catalog::Manager::GetInstance(); auto &rw_set = current_txn->GetRWSet(); //***************************************************** // we can optimize read-only transaction. if (current_txn->IsReadOnly() == true) { // validate read set. for (auto &tile_group_entry : rw_set) { oid_t tile_group_id = tile_group_entry.first; auto tile_group = manager.GetTileGroup(tile_group_id); auto tile_group_header = tile_group->GetHeader(); for (auto &tuple_entry : tile_group_entry.second) { auto tuple_slot = tuple_entry.first; // if this tuple is not newly inserted. if (tuple_entry.second == RW_TYPE_READ) { // Release read locks if (pessimistic_released_rdlock.find(tile_group_id) == pessimistic_released_rdlock.end() || pessimistic_released_rdlock[tile_group_id].find(tuple_slot) == pessimistic_released_rdlock[tile_group_id].end()) { ReleaseReadLock(tile_group_header, tuple_slot); pessimistic_released_rdlock[tile_group_id].insert(tuple_slot); } } else { assert(tuple_entry.second == RW_TYPE_INS_DEL); } } } // is it always true??? Result ret = current_txn->GetResult(); EndTransaction(); return ret; } //***************************************************** // generate transaction id. cid_t end_commit_id = GetNextCommitId(); auto &log_manager = logging::LogManager::GetInstance(); log_manager.LogBeginTransaction(end_commit_id); // install everything. for (auto &tile_group_entry : rw_set) { oid_t tile_group_id = tile_group_entry.first; auto tile_group = manager.GetTileGroup(tile_group_id); auto tile_group_header = tile_group->GetHeader(); for (auto &tuple_entry : tile_group_entry.second) { auto tuple_slot = tuple_entry.first; if (tuple_entry.second == RW_TYPE_READ) { // Release read locks if (pessimistic_released_rdlock.find(tile_group_id) == pessimistic_released_rdlock.end() || pessimistic_released_rdlock[tile_group_id].find(tuple_slot) == pessimistic_released_rdlock[tile_group_id].end()) { ReleaseReadLock(tile_group_header, tuple_slot); pessimistic_released_rdlock[tile_group_id].insert(tuple_slot); } } else if (tuple_entry.second == RW_TYPE_UPDATE) { // we must guarantee that, at any time point, only one version is // visible. ItemPointer new_version = tile_group_header->GetNextItemPointer(tuple_slot); ItemPointer old_version(tile_group_id, tuple_slot); // logging. log_manager.LogUpdate(current_txn, end_commit_id, old_version, new_version); auto new_tile_group_header = manager.GetTileGroup(new_version.block)->GetHeader(); new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID); new_tile_group_header->SetBeginCommitId(new_version.offset, end_commit_id); COMPILER_MEMORY_FENCE; tile_group_header->SetEndCommitId(tuple_slot, end_commit_id); COMPILER_MEMORY_FENCE; new_tile_group_header->SetTransactionId(new_version.offset, INITIAL_TXN_ID); tile_group_header->SetTransactionId(tuple_slot, INITIAL_TXN_ID); } else if (tuple_entry.second == RW_TYPE_DELETE) { ItemPointer new_version = tile_group_header->GetNextItemPointer(tuple_slot); ItemPointer delete_location(tile_group_id, tuple_slot); // logging. log_manager.LogDelete(end_commit_id, delete_location); // we do not change begin cid for old tuple. auto new_tile_group_header = manager.GetTileGroup(new_version.block)->GetHeader(); new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID); new_tile_group_header->SetBeginCommitId(new_version.offset, end_commit_id); COMPILER_MEMORY_FENCE; tile_group_header->SetEndCommitId(tuple_slot, end_commit_id); COMPILER_MEMORY_FENCE; new_tile_group_header->SetTransactionId(new_version.offset, INVALID_TXN_ID); tile_group_header->SetTransactionId(tuple_slot, INITIAL_TXN_ID); } else if (tuple_entry.second == RW_TYPE_INSERT) { assert(tile_group_header->GetTransactionId(tuple_slot) == current_txn->GetTransactionId()); // set the begin commit id to persist insert ItemPointer insert_location(tile_group_id, tuple_slot); log_manager.LogInsert(current_txn, end_commit_id, insert_location); tile_group_header->SetEndCommitId(tuple_slot, MAX_CID); tile_group_header->SetBeginCommitId(tuple_slot, end_commit_id); COMPILER_MEMORY_FENCE; tile_group_header->SetTransactionId(tuple_slot, INITIAL_TXN_ID); } else if (tuple_entry.second == RW_TYPE_INS_DEL) { assert(tile_group_header->GetTransactionId(tuple_slot) == current_txn->GetTransactionId()); tile_group_header->SetEndCommitId(tuple_slot, MAX_CID); tile_group_header->SetBeginCommitId(tuple_slot, MAX_CID); COMPILER_MEMORY_FENCE; // set the begin commit id to persist insert tile_group_header->SetTransactionId(tuple_slot, INVALID_TXN_ID); } } } log_manager.LogCommitTransaction(end_commit_id); EndTransaction(); pessimistic_released_rdlock.clear(); return Result::RESULT_SUCCESS; }
bool HybridScanExecutor::ExecPrimaryIndexLookup() { PL_ASSERT(index_done_ == false); const planner::HybridScanPlan &node = GetPlanNode<planner::HybridScanPlan>(); bool acquire_owner = GetPlanNode<planner::AbstractScan>().IsForUpdate(); auto key_column_ids_ = node.GetKeyColumnIds(); auto expr_type_ = node.GetExprTypes(); std::vector<ItemPointer *> tuple_location_ptrs; PL_ASSERT(index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY); if (0 == key_column_ids_.size()) { LOG_TRACE("Scan all keys"); index_->ScanAllKeys(tuple_location_ptrs); } else { LOG_TRACE("Scan"); index_->Scan(values_, key_column_ids_, expr_type_, SCAN_DIRECTION_TYPE_FORWARD, tuple_location_ptrs, &node.GetIndexPredicate().GetConjunctionList()[0]); } LOG_TRACE("Result tuple count: %lu", tuple_location_ptrs.size()); auto &transaction_manager = concurrency::TransactionManagerFactory::GetInstance(); auto current_txn = executor_context_->GetTransaction(); if (tuple_location_ptrs.size() == 0) { index_done_ = true; return false; } std::map<oid_t, std::vector<oid_t>> visible_tuples; // for every tuple that is found in the index. for (auto tuple_location_ptr : tuple_location_ptrs) { ItemPointer tuple_location = *tuple_location_ptr; if (type_ == HYBRID_SCAN_TYPE_HYBRID && tuple_location.block >= (block_threshold)) { item_pointers_.insert(tuple_location); } auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuple_location.block); auto tile_group_header = tile_group.get()->GetHeader(); // perform transaction read size_t chain_length = 0; while (true) { ++chain_length; auto visibility = transaction_manager.IsVisible(current_txn, tile_group_header, tuple_location.offset); if (visibility == VISIBILITY_OK) { visible_tuples[tuple_location.block].push_back(tuple_location.offset); auto res = transaction_manager.PerformRead(current_txn, tuple_location, acquire_owner); if (!res) { transaction_manager.SetTransactionResult(current_txn, RESULT_FAILURE); return res; } break; } else { ItemPointer old_item = tuple_location; cid_t old_end_cid = tile_group_header->GetEndCommitId(old_item.offset); tuple_location = tile_group_header->GetNextItemPointer(old_item.offset); // there must exist a visible version. assert(tuple_location.IsNull() == false); cid_t max_committed_cid = transaction_manager.GetMaxCommittedCid(); // check whether older version is garbage. if (old_end_cid < max_committed_cid) { assert(tile_group_header->GetTransactionId(old_item.offset) == INITIAL_TXN_ID || tile_group_header->GetTransactionId(old_item.offset) == INVALID_TXN_ID); if (tile_group_header->SetAtomicTransactionId( old_item.offset, INVALID_TXN_ID) == true) { // atomically swap item pointer held in the index bucket. AtomicUpdateItemPointer(tuple_location_ptr, tuple_location); } } tile_group = manager.GetTileGroup(tuple_location.block); tile_group_header = tile_group.get()->GetHeader(); } } } // Construct a logical tile for each block for (auto tuples : visible_tuples) { auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuples.first); std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile()); // Add relevant columns to logical tile logical_tile->AddColumns(tile_group, full_column_ids_); logical_tile->AddPositionList(std::move(tuples.second)); if (column_ids_.size() != 0) { logical_tile->ProjectColumns(full_column_ids_, column_ids_); } result_.push_back(logical_tile.release()); } index_done_ = true; LOG_TRACE("Result tiles : %lu", result_.size()); return true; }