TileGroupHeader::TileGroupHeader(const BackendType &backend_type, const int &tuple_count) : backend_type(backend_type), data(nullptr), num_tuple_slots(tuple_count), next_tuple_slot(0), tile_header_lock() { header_size = num_tuple_slots * header_entry_size; // allocate storage space for header auto &storage_manager = storage::StorageManager::GetInstance(); data = reinterpret_cast<char *>( storage_manager.Allocate(backend_type, header_size)); assert(data != nullptr); // zero out the data std::memset(data, 0, header_size); // Set MVCC Initial Value for (oid_t tuple_slot_id = START_OID; tuple_slot_id < num_tuple_slots; tuple_slot_id++) { SetTransactionId(tuple_slot_id, INVALID_TXN_ID); SetBeginCommitId(tuple_slot_id, MAX_CID); SetEndCommitId(tuple_slot_id, MAX_CID); SetNextItemPointer(tuple_slot_id, INVALID_ITEMPOINTER); SetPrevItemPointer(tuple_slot_id, INVALID_ITEMPOINTER); SetInsertCommit(tuple_slot_id, false); // unused SetDeleteCommit(tuple_slot_id, false); // unused } }
// Return false if the tuple's table (tile group) is dropped. // In such case, this recycled tuple can not be added to the recycled_list. // Since no one will use it any more, keeping track of it is useless. // Note that, if we drop a single tile group without dropping the whole table, // such assumption is problematic. bool GCManager::ResetTuple(const TupleMetadata &tuple_metadata) { auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(tuple_metadata.tile_group_id); // During the resetting, a table may deconstruct because of the DROP TABLE request if (tile_group == nullptr) { LOG_TRACE("Garbage tuple(%u, %u) in table %u no longer exists", tuple_metadata.tile_group_id, tuple_metadata.tuple_slot_id, tuple_metadata.table_id); return false; } // From now on, the tile group shared pointer is held by us // It's safe to set headers from now on. auto tile_group_header = tile_group->GetHeader(); // Reset the header tile_group_header->SetTransactionId(tuple_metadata.tuple_slot_id, INVALID_TXN_ID); tile_group_header->SetBeginCommitId(tuple_metadata.tuple_slot_id, MAX_CID); tile_group_header->SetEndCommitId(tuple_metadata.tuple_slot_id, MAX_CID); tile_group_header->SetPrevItemPointer(tuple_metadata.tuple_slot_id, INVALID_ITEMPOINTER); tile_group_header->SetNextItemPointer(tuple_metadata.tuple_slot_id, INVALID_ITEMPOINTER); PL_MEMSET( tile_group_header->GetReservedFieldRef(tuple_metadata.tuple_slot_id), 0, storage::TileGroupHeader::GetReservedSize()); LOG_TRACE("Garbage tuple(%u, %u) in table %u is reset", tuple_metadata.tile_group_id, tuple_metadata.tuple_slot_id, tuple_metadata.table_id); return true; }
bool PessimisticTxnManager::PerformDelete(const oid_t &tile_group_id, const oid_t &tuple_id, const ItemPointer &new_location) { LOG_TRACE("Performing Delete"); auto transaction_id = current_txn->GetTransactionId(); auto tile_group_header = catalog::Manager::GetInstance().GetTileGroup(tile_group_id)->GetHeader(); auto new_tile_group_header = catalog::Manager::GetInstance() .GetTileGroup(new_location.block)->GetHeader(); assert(tile_group_header->GetTransactionId(tuple_id) == transaction_id); assert(new_tile_group_header->GetTransactionId(new_location.offset) == INVALID_TXN_ID); assert(new_tile_group_header->GetBeginCommitId(new_location.offset) == MAX_CID); assert(new_tile_group_header->GetEndCommitId(new_location.offset) == MAX_CID); // Set up double linked list tile_group_header->SetNextItemPointer(tuple_id, new_location); new_tile_group_header->SetPrevItemPointer( new_location.offset, ItemPointer(tile_group_id, tuple_id)); new_tile_group_header->SetTransactionId(new_location.offset, transaction_id); new_tile_group_header->SetEndCommitId(new_location.offset, INVALID_CID); current_txn->RecordDelete(tile_group_id, tuple_id); return true; }
bool PessimisticTxnManager::PerformUpdate(const oid_t &tile_group_id, const oid_t &tuple_id, const ItemPointer &new_location) { LOG_INFO("Performing Write %lu %lu", tile_group_id, tuple_id); auto transaction_id = current_txn->GetTransactionId(); auto tile_group_header = catalog::Manager::GetInstance().GetTileGroup(tile_group_id)->GetHeader(); auto new_tile_group_header = catalog::Manager::GetInstance() .GetTileGroup(new_location.block)->GetHeader(); // if we can perform update, then we must have already locked the older // version. assert(tile_group_header->GetTransactionId(tuple_id) == transaction_id); assert(new_tile_group_header->GetTransactionId(new_location.offset) == INVALID_TXN_ID); assert(new_tile_group_header->GetBeginCommitId(new_location.offset) == MAX_CID); assert(new_tile_group_header->GetEndCommitId(new_location.offset) == MAX_CID); tile_group_header->SetTransactionId(tuple_id, transaction_id); // The write lock must have been acquired // Notice: 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->SetNextItemPointer(tuple_id, new_location); new_tile_group_header->SetPrevItemPointer( new_location.offset, ItemPointer(tile_group_id, tuple_id)); new_tile_group_header->SetTransactionId(new_location.offset, transaction_id); // Add the old tuple into the update set current_txn->RecordUpdate(tile_group_id, tuple_id); return true; }
bool TransactionLevelGCManager::ResetTuple(const ItemPointer &location) { auto &manager = catalog::Manager::GetInstance(); auto tile_group = manager.GetTileGroup(location.block).get(); auto tile_group_header = tile_group->GetHeader(); // Reset the header tile_group_header->SetTransactionId(location.offset, INVALID_TXN_ID); tile_group_header->SetBeginCommitId(location.offset, MAX_CID); tile_group_header->SetEndCommitId(location.offset, MAX_CID); tile_group_header->SetPrevItemPointer(location.offset, INVALID_ITEMPOINTER); tile_group_header->SetNextItemPointer(location.offset, INVALID_ITEMPOINTER); PL_MEMSET( tile_group_header->GetReservedFieldRef(location.offset), 0, storage::TileGroupHeader::GetReservedSize()); // Reclaim the varlen pool CheckAndReclaimVarlenColumns(tile_group, location.offset); LOG_TRACE("Garbage tuple(%u, %u) is reset", location.block, location.offset); return true; }
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; }
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); } }