diff --git a/src/concurrency/transaction_manager_impl.cpp b/src/concurrency/transaction_manager_impl.cpp index 318c1fd6c..781b575bf 100644 --- a/src/concurrency/transaction_manager_impl.cpp +++ b/src/concurrency/transaction_manager_impl.cpp @@ -27,19 +27,6 @@ namespace bustub { auto TransactionManager::UpdateUndoLink(RID rid, std::optional prev_link, std::function)> &&check) -> bool { - std::function)> wrapper_func = - [check](std::optional link) -> bool { - if (link.has_value()) { - return check(link->prev_); - } - return check(std::nullopt); - }; - return UpdateVersionLink(rid, prev_link.has_value() ? std::make_optional(VersionUndoLink{*prev_link}) : std::nullopt, - check != nullptr ? wrapper_func : nullptr); -} - -auto TransactionManager::UpdateVersionLink(RID rid, std::optional prev_version, - std::function)> &&check) -> bool { std::unique_lock lck(version_info_mutex_); std::shared_ptr pg_ver_info = nullptr; auto iter = version_info_.find(rid.GetPageId()); @@ -51,8 +38,8 @@ auto TransactionManager::UpdateVersionLink(RID rid, std::optional lck2(pg_ver_info->mutex_); lck.unlock(); - auto iter2 = pg_ver_info->prev_version_.find(rid.GetSlotNum()); - if (iter2 == pg_ver_info->prev_version_.end()) { + auto iter2 = pg_ver_info->prev_link_.find(rid.GetSlotNum()); + if (iter2 == pg_ver_info->prev_link_.end()) { if (check != nullptr && !check(std::nullopt)) { return false; } @@ -61,15 +48,15 @@ auto TransactionManager::UpdateVersionLink(RID rid, std::optionalprev_version_[rid.GetSlotNum()] = *prev_version; + if (prev_link.has_value()) { + pg_ver_info->prev_link_[rid.GetSlotNum()] = *prev_link; } else { - pg_ver_info->prev_version_.erase(rid.GetSlotNum()); + pg_ver_info->prev_link_.erase(rid.GetSlotNum()); } return true; } -auto TransactionManager::GetVersionLink(RID rid) -> std::optional { +auto TransactionManager::GetUndoLink(RID rid) -> std::optional { std::shared_lock lck(version_info_mutex_); auto iter = version_info_.find(rid.GetPageId()); if (iter == version_info_.end()) { @@ -78,21 +65,13 @@ auto TransactionManager::GetVersionLink(RID rid) -> std::optional pg_ver_info = iter->second; std::unique_lock lck2(pg_ver_info->mutex_); lck.unlock(); - auto iter2 = pg_ver_info->prev_version_.find(rid.GetSlotNum()); - if (iter2 == pg_ver_info->prev_version_.end()) { + auto iter2 = pg_ver_info->prev_link_.find(rid.GetSlotNum()); + if (iter2 == pg_ver_info->prev_link_.end()) { return std::nullopt; } return std::make_optional(iter2->second); } -auto TransactionManager::GetUndoLink(RID rid) -> std::optional { - auto version_link = GetVersionLink(rid); - if (version_link.has_value()) { - return version_link->prev_; - } - return std::nullopt; -} - auto TransactionManager::GetUndoLogOptional(UndoLink link) -> std::optional { std::shared_lock lck(txn_map_mutex_); auto iter = txn_map_.find(link.prev_txn_); @@ -122,4 +101,36 @@ void Transaction::SetTainted() { std::terminate(); } +auto UpdateTupleAndUndoLink( + TransactionManager *txn_mgr, RID rid, std::optional undo_link, TableHeap *table_heap, Transaction *txn, + const TupleMeta &meta, const Tuple &tuple, + std::function)> &&check) -> bool { + auto page_write_guard = table_heap->AcquireTablePageWriteLock(rid); + auto page = page_write_guard.AsMut(); + + auto [base_meta, base_tuple] = page->GetTuple(rid); + if (check != nullptr && !check(base_meta, base_tuple, rid, undo_link)) { + return false; + } + + // Update tuple and tupleMeta if pass in tuple and meta are different + if (meta != base_meta || !IsTupleContentEqual(tuple, base_tuple)) { + table_heap->UpdateTupleInPlaceWithLockAcquired(meta, tuple, rid, page); + } + + txn_mgr->UpdateUndoLink(rid, undo_link); + + return true; +} + +auto GetTupleAndUndoLink(TransactionManager *txn_mgr, TableHeap *table_heap, RID rid) + -> std::tuple> { + auto page_read_guard = table_heap->AcquireTablePageReadLock(rid); + auto page = page_read_guard.As(); + auto [meta, tuple] = page->GetTuple(rid); + + auto undo_link = txn_mgr->GetUndoLink(rid); + return std::make_tuple(meta, tuple, undo_link); +} + } // namespace bustub diff --git a/src/include/concurrency/transaction_manager.h b/src/include/concurrency/transaction_manager.h index 6a33ffaf8..f6e9c9a6b 100644 --- a/src/include/concurrency/transaction_manager.h +++ b/src/include/concurrency/transaction_manager.h @@ -18,6 +18,7 @@ #include // NOLINT #include #include +#include #include #include @@ -26,32 +27,10 @@ #include "concurrency/transaction.h" #include "concurrency/watermark.h" #include "recovery/log_manager.h" +#include "storage/table/table_heap.h" #include "storage/table/tuple.h" namespace bustub { - -/// The first undo link in the version chain, that links table heap tuple to the undo log. -struct VersionUndoLink { - /** The next version in the version chain. */ - UndoLink prev_; - /** Whether a transaction is modifying the version link. Fall 2023: you do not need to read / write this field until - * task 4.2. */ - bool in_progress_{false}; - - friend auto operator==(const VersionUndoLink &a, const VersionUndoLink &b) { - return a.prev_ == b.prev_ && a.in_progress_ == b.in_progress_; - } - - friend auto operator!=(const VersionUndoLink &a, const VersionUndoLink &b) { return !(a == b); } - - inline static auto FromOptionalUndoLink(std::optional undo_link) -> std::optional { - if (undo_link.has_value()) { - return VersionUndoLink{*undo_link}; - } - return std::nullopt; - } -}; - /** * TransactionManager keeps track of all the transactions running in the system. */ @@ -81,25 +60,15 @@ class TransactionManager { void Abort(Transaction *txn); /** - * @brief Use this function before task 4.2. Update an undo link that links table heap tuple to the first undo log. + * @brief Update an undo link that links table heap tuple to the first undo log. * Before updating, `check` function will be called to ensure validity. */ auto UpdateUndoLink(RID rid, std::optional prev_link, std::function)> &&check = nullptr) -> bool; - /** - * @brief Use this function after task 4.2. Update an undo link that links table heap tuple to the first undo log. - * Before updating, `check` function will be called to ensure validity. - */ - auto UpdateVersionLink(RID rid, std::optional prev_version, - std::function)> &&check = nullptr) -> bool; - - /** @brief Get the first undo log of a table heap tuple. Use this before task 4.2 */ + /** @brief Get the first undo log of a table heap tuple. */ auto GetUndoLink(RID rid) -> std::optional; - /** @brief Get the first undo log of a table heap tuple. Use this after task 4.2 */ - auto GetVersionLink(RID rid) -> std::optional; - /** @brief Access the transaction undo log buffer and get the undo log. Return nullopt if the txn does not exist. Will * still throw an exception if the index is out of range. */ auto GetUndoLogOptional(UndoLink link) -> std::optional; @@ -127,7 +96,7 @@ class TransactionManager { /** Stores previous version info for all slots. Note: DO NOT use `[x]` to access it because * it will create new elements even if it does not exist. Use `find` instead. */ - std::unordered_map prev_version_; + std::unordered_map prev_link_; }; /** protects version info */ @@ -155,4 +124,19 @@ class TransactionManager { auto VerifyTxn(Transaction *txn) -> bool; }; +/** + * @brief Update the tuple and its undo link in the table heap atomically. + */ +auto UpdateTupleAndUndoLink( + TransactionManager *txn_mgr, RID rid, std::optional undo_link, TableHeap *table_heap, Transaction *txn, + const TupleMeta &meta, const Tuple &tuple, + std::function)> &&check = nullptr) + -> bool; + +/** + * @brief Get the tuple and its undo link in the table heap atomically. + */ +auto GetTupleAndUndoLink(TransactionManager *txn_mgr, TableHeap *table_heap, RID rid) + -> std::tuple>; + } // namespace bustub diff --git a/test/txn/txn_scan_test.cpp b/test/txn/txn_scan_test.cpp index 5b2d40bbe..4a2acd64e 100644 --- a/test/txn/txn_scan_test.cpp +++ b/test/txn/txn_scan_test.cpp @@ -188,16 +188,16 @@ TEST(TxnScanTest, DISABLED_ScanTest) { // NOLINT auto rid1 = *table_info->table_->InsertTuple(TupleMeta{txn4->GetTransactionTempTs(), false}, Tuple{{Int(1), DoubleNull(), BoolNull()}, schema.get()}); - bustub->txn_manager_->UpdateVersionLink(rid1, VersionUndoLink{prev_log_1}, nullptr); + bustub->txn_manager_->UpdateUndoLink(rid1, prev_log_1, nullptr); auto rid2 = *table_info->table_->InsertTuple(TupleMeta{txn3->GetReadTs(), false}, Tuple{{Int(3), DoubleNull(), BoolNull()}, schema.get()}); - bustub->txn_manager_->UpdateVersionLink(rid2, VersionUndoLink{prev_log_2}, nullptr); + bustub->txn_manager_->UpdateUndoLink(rid2, prev_log_2, nullptr); auto rid3 = *table_info->table_->InsertTuple(TupleMeta{txn4->GetReadTs(), true}, Tuple{{IntNull(), DoubleNull(), BoolNull()}, schema.get()}); - bustub->txn_manager_->UpdateVersionLink(rid3, VersionUndoLink{prev_log_4}, nullptr); + bustub->txn_manager_->UpdateUndoLink(rid3, prev_log_4, nullptr); auto rid4 = *table_info->table_->InsertTuple(TupleMeta{txn3->GetTransactionTempTs(), true}, Tuple{{IntNull(), DoubleNull(), BoolNull()}, schema.get()}); - bustub->txn_manager_->UpdateVersionLink(rid4, VersionUndoLink{prev_log_5}, nullptr); + bustub->txn_manager_->UpdateUndoLink(rid4, prev_log_5, nullptr); TxnMgrDbg("before verify scan", bustub->txn_manager_.get(), table_info, table_info->table_.get());