Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Spring24 project4 starter code modification #709

Merged
merged 3 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 40 additions & 29 deletions src/concurrency/transaction_manager_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,6 @@ namespace bustub {

auto TransactionManager::UpdateUndoLink(RID rid, std::optional<UndoLink> prev_link,
std::function<bool(std::optional<UndoLink>)> &&check) -> bool {
std::function<bool(std::optional<VersionUndoLink>)> wrapper_func =
[check](std::optional<VersionUndoLink> 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<VersionUndoLink> prev_version,
std::function<bool(std::optional<VersionUndoLink>)> &&check) -> bool {
std::unique_lock<std::shared_mutex> lck(version_info_mutex_);
std::shared_ptr<PageVersionInfo> pg_ver_info = nullptr;
auto iter = version_info_.find(rid.GetPageId());
Expand All @@ -51,8 +38,8 @@ auto TransactionManager::UpdateVersionLink(RID rid, std::optional<VersionUndoLin
}
std::unique_lock<std::shared_mutex> 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;
}
Expand All @@ -61,15 +48,15 @@ auto TransactionManager::UpdateVersionLink(RID rid, std::optional<VersionUndoLin
return false;
}
}
if (prev_version.has_value()) {
pg_ver_info->prev_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<VersionUndoLink> {
auto TransactionManager::GetUndoLink(RID rid) -> std::optional<UndoLink> {
std::shared_lock<std::shared_mutex> lck(version_info_mutex_);
auto iter = version_info_.find(rid.GetPageId());
if (iter == version_info_.end()) {
Expand All @@ -78,21 +65,13 @@ auto TransactionManager::GetVersionLink(RID rid) -> std::optional<VersionUndoLin
std::shared_ptr<PageVersionInfo> pg_ver_info = iter->second;
std::unique_lock<std::shared_mutex> 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<UndoLink> {
auto version_link = GetVersionLink(rid);
if (version_link.has_value()) {
return version_link->prev_;
}
return std::nullopt;
}

auto TransactionManager::GetUndoLogOptional(UndoLink link) -> std::optional<UndoLog> {
std::shared_lock<std::shared_mutex> lck(txn_map_mutex_);
auto iter = txn_map_.find(link.prev_txn_);
Expand Down Expand Up @@ -122,4 +101,36 @@ void Transaction::SetTainted() {
std::terminate();
}

auto UpdateTupleAndUndoLink(
TransactionManager *txn_mgr, RID rid, std::optional<UndoLink> undo_link, TableHeap *table_heap, Transaction *txn,
const TupleMeta &meta, const Tuple &tuple,
std::function<bool(const TupleMeta &meta, const Tuple &tuple, RID rid, std::optional<UndoLink>)> &&check) -> bool {
auto page_write_guard = table_heap->AcquireTablePageWriteLock(rid);
auto page = page_write_guard.AsMut<TablePage>();

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<TupleMeta, Tuple, std::optional<UndoLink>> {
auto page_read_guard = table_heap->AcquireTablePageReadLock(rid);
auto page = page_read_guard.As<TablePage>();
auto [meta, tuple] = page->GetTuple(rid);

auto undo_link = txn_mgr->GetUndoLink(rid);
return std::make_tuple(meta, tuple, undo_link);
}

} // namespace bustub
56 changes: 20 additions & 36 deletions src/include/concurrency/transaction_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <mutex> // NOLINT
#include <optional>
#include <shared_mutex>
#include <tuple>
#include <unordered_map>
#include <unordered_set>

Expand All @@ -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<UndoLink> undo_link) -> std::optional<VersionUndoLink> {
if (undo_link.has_value()) {
return VersionUndoLink{*undo_link};
}
return std::nullopt;
}
};

/**
* TransactionManager keeps track of all the transactions running in the system.
*/
Expand Down Expand Up @@ -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<UndoLink> prev_link,
std::function<bool(std::optional<UndoLink>)> &&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<VersionUndoLink> prev_version,
std::function<bool(std::optional<VersionUndoLink>)> &&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<UndoLink>;

/** @brief Get the first undo log of a table heap tuple. Use this after task 4.2 */
auto GetVersionLink(RID rid) -> std::optional<VersionUndoLink>;

/** @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<UndoLog>;
Expand Down Expand Up @@ -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<slot_offset_t, VersionUndoLink> prev_version_;
std::unordered_map<slot_offset_t, UndoLink> prev_link_;
};

/** protects version info */
Expand Down Expand Up @@ -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<UndoLink> undo_link, TableHeap *table_heap, Transaction *txn,
const TupleMeta &meta, const Tuple &tuple,
std::function<bool(const TupleMeta &meta, const Tuple &tuple, RID rid, std::optional<UndoLink>)> &&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<TupleMeta, Tuple, std::optional<UndoLink>>;

} // namespace bustub
8 changes: 4 additions & 4 deletions test/txn/txn_scan_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
Loading