void TransactionLevelGCManager::DeleteFromIndexes(const std::shared_ptr<GarbageContext>& garbage_ctx) { GCSetType gc_set_type = garbage_ctx->gc_set_type_; if (gc_set_type == GC_SET_TYPE_COMMITTED) { // if the transaction is committed, // then we need to remove tuples that are deleted by the transaction from indexes. for (auto entry : *(garbage_ctx->gc_set_.get())) { for (auto &element : entry.second) { if (element.second == RW_TYPE_DELETE || element.second == RW_TYPE_INS_DEL) { // only old versions are stored in the gc set. // so we can safely get indirection from the indirection array. auto tile_group = catalog::Manager::GetInstance().GetTileGroup(entry.first); if (tile_group != nullptr){ auto tile_group_header = catalog::Manager::GetInstance() .GetTileGroup(entry.first) ->GetHeader(); ItemPointer *indirection = tile_group_header->GetIndirection(element.first); DeleteTupleFromIndexes(indirection); } } } } } else { PL_ASSERT(gc_set_type == GC_SET_TYPE_ABORTED); for (auto entry : *(garbage_ctx->gc_set_.get())) { for (auto &element : entry.second) { if (element.second == RW_TYPE_INSERT || element.second == RW_TYPE_INS_DEL) { auto tile_group_header = catalog::Manager::GetInstance() .GetTileGroup(entry.first) ->GetHeader(); ItemPointer *indirection = tile_group_header->GetIndirection(element.first); DeleteTupleFromIndexes(indirection); } } } } }
void TimestampOrderingTransactionManager::PerformUpdate( TransactionContext *const current_txn, const ItemPointer &location, const ItemPointer &new_location) { PL_ASSERT(current_txn->GetIsolationLevel() != IsolationLevelType::READ_ONLY); ItemPointer old_location = location; LOG_TRACE("Performing Update old tuple %u %u", old_location.block, old_location.offset); LOG_TRACE("Performing Update new tuple %u %u", new_location.block, new_location.offset); auto &manager = catalog::Manager::GetInstance(); auto tile_group_header = manager.GetTileGroup(old_location.block)->GetHeader(); auto new_tile_group_header = manager.GetTileGroup(new_location.block)->GetHeader(); auto transaction_id = current_txn->GetTransactionId(); // if we can perform update, then we must have already locked the older // version. PL_ASSERT(tile_group_header->GetTransactionId(old_location.offset) == transaction_id); PL_ASSERT(tile_group_header->GetPrevItemPointer(old_location.offset) .IsNull() == true); // check whether the new version is empty. PL_ASSERT(new_tile_group_header->GetTransactionId(new_location.offset) == INVALID_TXN_ID); PL_ASSERT(new_tile_group_header->GetBeginCommitId(new_location.offset) == MAX_CID); PL_ASSERT(new_tile_group_header->GetEndCommitId(new_location.offset) == MAX_CID); // if the executor doesn't call PerformUpdate after AcquireOwnership, // no one will possibly release the write lock acquired by this txn. // Set double linked list tile_group_header->SetPrevItemPointer(old_location.offset, new_location); new_tile_group_header->SetNextItemPointer(new_location.offset, old_location); new_tile_group_header->SetTransactionId(new_location.offset, transaction_id); // we should guarantee that the newer version is all set before linking the // newer version to older version. COMPILER_MEMORY_FENCE; InitTupleReserved(new_tile_group_header, new_location.offset); // we must be updating the latest version. // Set the header information for the new version ItemPointer *index_entry_ptr = tile_group_header->GetIndirection(old_location.offset); // if there's no primary index on a table, then index_entry_ptr == nullptr. if (index_entry_ptr != nullptr) { new_tile_group_header->SetIndirection(new_location.offset, index_entry_ptr); // Set the index header in an atomic way. // We do it atomically because we don't want any one to see a half-done // pointer. // In case of contention, no one can update this pointer when we are // updating it // because we are holding the write lock. This update should success in // its first trial. UNUSED_ATTRIBUTE auto res = AtomicUpdateItemPointer(index_entry_ptr, new_location); PL_ASSERT(res == true); } // Add the old tuple into the update set current_txn->RecordUpdate(old_location); // Increment table update op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableUpdates( new_location.block); } }
bool TimestampOrderingTransactionManager::PerformRead( TransactionContext *const current_txn, const ItemPointer &read_location, bool acquire_ownership) { ItemPointer location = read_location; ////////////////////////////////////////////////////////// //// handle READ_ONLY ////////////////////////////////////////////////////////// if (current_txn->GetIsolationLevel() == IsolationLevelType::READ_ONLY) { // do not update read set for read-only transactions. return true; } // end READ ONLY ////////////////////////////////////////////////////////// //// handle SNAPSHOT ////////////////////////////////////////////////////////// // TODO: what if we want to read a version that we write? else if (current_txn->GetIsolationLevel() == IsolationLevelType::SNAPSHOT) { oid_t tile_group_id = location.block; oid_t tuple_id = location.offset; LOG_TRACE("PerformRead (%u, %u)\n", location.block, location.offset); auto &manager = catalog::Manager::GetInstance(); auto tile_group_header = manager.GetTileGroup(tile_group_id)->GetHeader(); // Check if it's select for update before we check the ownership // and modify the last reader cid if (acquire_ownership == true) { // get the latest version of this tuple. location = *(tile_group_header->GetIndirection(location.offset)); tile_group_id = location.block; tuple_id = location.offset; tile_group_header = manager.GetTileGroup(tile_group_id)->GetHeader(); if (IsOwner(current_txn, tile_group_header, tuple_id) == false) { // Acquire ownership if we haven't if (IsOwnable(current_txn, tile_group_header, tuple_id) == false) { // Cannot own return false; } if (AcquireOwnership(current_txn, tile_group_header, tuple_id) == false) { // Cannot acquire ownership return false; } // Record RWType::READ_OWN current_txn->RecordReadOwn(location); } // if we have already owned the version. PL_ASSERT(IsOwner(current_txn, tile_group_header, tuple_id) == true); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } else { // if it's not select for update, then update read set and return true. current_txn->RecordRead(location); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } } // end SNAPSHOT ////////////////////////////////////////////////////////// //// handle READ_COMMITTED ////////////////////////////////////////////////////////// else if (current_txn->GetIsolationLevel() == IsolationLevelType::READ_COMMITTED) { oid_t tile_group_id = location.block; oid_t tuple_id = location.offset; LOG_TRACE("PerformRead (%u, %u)\n", location.block, location.offset); auto &manager = catalog::Manager::GetInstance(); auto tile_group_header = manager.GetTileGroup(tile_group_id)->GetHeader(); // Check if it's select for update before we check the ownership. if (acquire_ownership == true) { // acquire ownership. if (IsOwner(current_txn, tile_group_header, tuple_id) == false) { // Acquire ownership if we haven't if (IsOwnable(current_txn, tile_group_header, tuple_id) == false) { // Cannot own return false; } if (AcquireOwnership(current_txn, tile_group_header, tuple_id) == false) { // Cannot acquire ownership return false; } // Record RWType::READ_OWN current_txn->RecordReadOwn(location); } // if we have already owned the version. PL_ASSERT(IsOwner(current_txn, tile_group_header, tuple_id) == true); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } else { // a transaction can never read an uncommitted version. if (IsOwner(current_txn, tile_group_header, tuple_id) == false) { if (IsOwned(current_txn, tile_group_header, tuple_id) == false) { current_txn->RecordRead(location); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } else { // if the tuple has been owned by some concurrent transactions, // then read fails. LOG_TRACE("Transaction read failed"); return false; } } else { // this version must already be in the read/write set. // so no need to update read set. // current_txn->RecordRead(location); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } } } // end READ_COMMITTED ////////////////////////////////////////////////////////// //// handle SERIALIZABLE and REPEATABLE_READS ////////////////////////////////////////////////////////// else { PL_ASSERT(current_txn->GetIsolationLevel() == IsolationLevelType::SERIALIZABLE || current_txn->GetIsolationLevel() == IsolationLevelType::REPEATABLE_READS); oid_t tile_group_id = location.block; oid_t tuple_id = location.offset; LOG_TRACE("PerformRead (%u, %u)\n", location.block, location.offset); auto &manager = catalog::Manager::GetInstance(); auto tile_group_header = manager.GetTileGroup(tile_group_id)->GetHeader(); // Check if it's select for update before we check the ownership // and modify the last reader cid. if (acquire_ownership == true) { // acquire ownership. if (IsOwner(current_txn, tile_group_header, tuple_id) == false) { // Acquire ownership if we haven't if (IsOwnable(current_txn, tile_group_header, tuple_id) == false) { // Cannot own return false; } if (AcquireOwnership(current_txn, tile_group_header, tuple_id) == false) { // Cannot acquire ownership return false; } // Record RWType::READ_OWN current_txn->RecordReadOwn(location); // now we have already obtained the ownership. // then attempt to set last reader cid. UNUSED_ATTRIBUTE bool ret = SetLastReaderCommitId( tile_group_header, tuple_id, current_txn->GetCommitId(), true); PL_ASSERT(ret == true); // there's no need to maintain read set for timestamp ordering protocol. // T/O does not check the read set during commit phase. } // if we have already owned the version. PL_ASSERT(IsOwner(current_txn, tile_group_header, tuple_id) == true); PL_ASSERT(GetLastReaderCommitId(tile_group_header, tuple_id) == current_txn->GetCommitId() || GetLastReaderCommitId(tile_group_header, tuple_id) == 0); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } else { if (IsOwner(current_txn, tile_group_header, tuple_id) == false) { // if the current transaction does not own this tuple, // then attempt to set last reader cid. if (SetLastReaderCommitId(tile_group_header, tuple_id, current_txn->GetCommitId(), false) == true) { // update read set. current_txn->RecordRead(location); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } else { // if the tuple has been owned by some concurrent transactions, // then read fails. LOG_TRACE("Transaction read failed"); return false; } } else { // if the current transaction has already owned this tuple, // then perform read directly. PL_ASSERT(GetLastReaderCommitId(tile_group_header, tuple_id) == current_txn->GetCommitId() || GetLastReaderCommitId(tile_group_header, tuple_id) == 0); // this version must already be in the read/write set. // so no need to update read set. // current_txn->RecordRead(location); // Increment table read op stats if (static_cast<StatsType>(settings::SettingsManager::GetInt(settings::SettingId::stats_mode)) != StatsType::INVALID) { stats::BackendStatsContext::GetInstance()->IncrementTableReads( location.block); } return true; } } } // end SERIALIZABLE || REPEATABLE_READS }