void TimestampOrderingTransactionManager::PerformInsert( TransactionContext *const current_txn, const ItemPointer &location, ItemPointer *index_entry_ptr) { PELOTON_ASSERT(!current_txn->IsReadOnly()); oid_t tile_group_id = location.block; oid_t tuple_id = location.offset; auto storage_manager = storage::StorageManager::GetInstance(); auto tile_group_header = storage_manager->GetTileGroup(tile_group_id)->GetHeader(); auto transaction_id = current_txn->GetTransactionId(); // check MVCC info // the tuple slot must be empty. PELOTON_ASSERT(tile_group_header->GetTransactionId(tuple_id) == INVALID_TXN_ID); PELOTON_ASSERT(tile_group_header->GetBeginCommitId(tuple_id) == MAX_CID); PELOTON_ASSERT(tile_group_header->GetEndCommitId(tuple_id) == MAX_CID); tile_group_header->SetTransactionId(tuple_id, transaction_id); tile_group_header->SetLastReaderCommitId(tuple_id, current_txn->GetCommitId()); // no need to set next item pointer. // Add the new tuple into the insert set current_txn->RecordInsert(location); // Write down the head pointer's address in tile group header tile_group_header->SetIndirection(tuple_id, index_entry_ptr); }
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 }
void TimestampOrderingTransactionManager::PerformUpdate( TransactionContext *const current_txn, const ItemPointer &location, const ItemPointer &new_location) { PELOTON_ASSERT(!current_txn->IsReadOnly()); 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 storage_manager = storage::StorageManager::GetInstance(); auto tile_group_header = storage_manager->GetTileGroup(old_location.block)->GetHeader(); auto new_tile_group_header = storage_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. PELOTON_ASSERT(tile_group_header->GetTransactionId(old_location.offset) == transaction_id); PELOTON_ASSERT( tile_group_header->GetPrevItemPointer(old_location.offset).IsNull() == true); // check whether the new version is empty. PELOTON_ASSERT(new_tile_group_header->GetTransactionId(new_location.offset) == INVALID_TXN_ID); PELOTON_ASSERT(new_tile_group_header->GetBeginCommitId(new_location.offset) == MAX_CID); PELOTON_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); new_tile_group_header->SetLastReaderCommitId(new_location.offset, current_txn->GetCommitId()); // we should guarantee that the newer version is all set before linking the // newer version to older version. COMPILER_MEMORY_FENCE; // 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); PELOTON_ASSERT(res == true); } // Add the old tuple into the update set current_txn->RecordUpdate(old_location); }