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

Prepare Spring 24 P1 #690

Merged
merged 5 commits into from
Jan 27, 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
3 changes: 3 additions & 0 deletions src/include/buffer/buffer_pool_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "buffer/lru_k_replacer.h"
#include "common/config.h"
#include "recovery/log_manager.h"
#include "storage/disk/cow_buffer.h"
#include "storage/disk/disk_scheduler.h"
#include "storage/page/page.h"
#include "storage/page/page_guard.h"
Expand Down Expand Up @@ -192,6 +193,8 @@ class BufferPoolManager {
std::list<frame_id_t> free_list_;
/** This latch protects shared data structures. We recommend updating this comment to describe what it protects. */
std::mutex latch_;
/** This buffer is for the leaderboard task. You may want to use it to optimize the write requests. */
CoWBuffer cow_buffer_ __attribute__((__unused__));

/**
* @brief Allocate a page on disk. Caller should acquire the latch before calling this function.
Expand Down
97 changes: 97 additions & 0 deletions src/include/storage/disk/cow_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===----------------------------------------------------------------------===//
//
// BusTub
//
// cow_buffer.h
//
// Identification: src/include/storage/disk/cow_buffer.h
//
// Copyright (c) 2015-2023, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#pragma once

#include <cstdint>
#include <cstring>
#include "common/config.h"
#include "common/macros.h"
#include "storage/page/page.h"

namespace bustub {

/**
* CoWBuffer provides extra memory space other than the buffer pool to store the copy-on-write pages.
* It's purpose is to gather the copy of pages that are about to be written back to disk, so that the bpm
* doesn't have to incur IO penality and wait for the write to be completed when evicting.
* Spring 24: The buffer is limited to store a constant number of pages in total (8).
* !! ANY ATTEMPTS TO ADD ANOTHER IN-MEMORY CACHE WILL BE REVIEWED MANUALLY AS PER LEADERBOARD POLICY !!
*/
class CoWBuffer {
public:
CoWBuffer() : cow_pages_{new Page[8]} {}
~CoWBuffer() { delete[] cow_pages_; }
DISALLOW_COPY_AND_MOVE(CoWBuffer);

/**
* @brief Adds a new page to the CoW buffer.
* @param page the page pointer from the bpm that is about to be evicted.
* @return pointer to the copied page in the buffer, or nullptr if the buffer is full.
*/
auto Add(Page *page) -> Page * {
if ((page == nullptr) || IsFull()) {
return nullptr;
}

uint32_t slot = FindFreeSlot();
memcpy(cow_pages_[slot].GetData(), page->GetData(), BUSTUB_PAGE_SIZE);
MarkSlotUsed(slot);

return cow_pages_ + slot;
}

/**
* @brief Removes a page from the CoW buffer.
* @param page the pointer previously returned by Add.
*/
auto Remove(Page *page) -> void {
if (page != nullptr) {
MarkSlotFree(page - cow_pages_);
}
}

private:
/** @brief Whether the buffer is full. */
auto IsFull() -> bool { return free_slot_bitmap_ == 0xFFU; }

/** @brief Finds a free slot in the buffer, if not full. */
auto FindFreeSlot() -> uint32_t {
BUSTUB_ASSERT(!IsFull(), "no free slot in cow buffer");
uint32_t i = 0;
uint8_t bitmap = free_slot_bitmap_;
while ((bitmap & 1U) != 0) {
bitmap >>= 1;
i++;
}
return i;
}

/** @brief Marks a free slot as used. */
void MarkSlotUsed(uint32_t slot) {
BUSTUB_ASSERT(((free_slot_bitmap_ >> slot) & 1U) == 0, "slot has already been used");
free_slot_bitmap_ |= (1U << slot);
}

/** @brief Marks a used slot as free. */
void MarkSlotFree(uint32_t slot) {
BUSTUB_ASSERT(((free_slot_bitmap_ >> slot) & 1U) == 1, "slot is already free");
free_slot_bitmap_ &= ~(1U << slot);
}

/** The array of CoW buffer pages. */
Page *cow_pages_;
/** The bitmap that records which slots are free. */
uint8_t free_slot_bitmap_{0};
};

} // namespace bustub
30 changes: 15 additions & 15 deletions src/include/storage/page/page_guard.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class BasicPageGuard {
BasicPageGuard(const BasicPageGuard &) = delete;
auto operator=(const BasicPageGuard &) -> BasicPageGuard & = delete;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Move constructor for BasicPageGuard
*
Expand All @@ -29,7 +29,7 @@ class BasicPageGuard {
*/
BasicPageGuard(BasicPageGuard &&that) noexcept;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Drop a page guard
*
Expand All @@ -40,7 +40,7 @@ class BasicPageGuard {
*/
void Drop();

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Move assignment for BasicPageGuard
*
Expand All @@ -61,7 +61,7 @@ class BasicPageGuard {
*/
~BasicPageGuard();

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Upgrade a BasicPageGuard to a ReadPageGuard
*
Expand All @@ -72,7 +72,7 @@ class BasicPageGuard {
*/
auto UpgradeRead() -> ReadPageGuard;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Upgrade a BasicPageGuard to a WritePageGuard
*
Expand Down Expand Up @@ -114,11 +114,11 @@ class BasicPageGuard {
class ReadPageGuard {
public:
ReadPageGuard() = default;
ReadPageGuard(BufferPoolManager *bpm, Page *page) : guard_(bpm, page) {}
ReadPageGuard(BufferPoolManager *bpm, Page *page);
ReadPageGuard(const ReadPageGuard &) = delete;
auto operator=(const ReadPageGuard &) -> ReadPageGuard & = delete;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Move constructor for ReadPageGuard
*
Expand All @@ -128,7 +128,7 @@ class ReadPageGuard {
*/
ReadPageGuard(ReadPageGuard &&that) noexcept;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Move assignment for ReadPageGuard
*
Expand All @@ -137,7 +137,7 @@ class ReadPageGuard {
*/
auto operator=(ReadPageGuard &&that) noexcept -> ReadPageGuard &;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Drop a ReadPageGuard
*
Expand All @@ -148,7 +148,7 @@ class ReadPageGuard {
*/
void Drop();

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Destructor for ReadPageGuard
*
Expand All @@ -174,11 +174,11 @@ class ReadPageGuard {
class WritePageGuard {
public:
WritePageGuard() = default;
WritePageGuard(BufferPoolManager *bpm, Page *page) : guard_(bpm, page) {}
WritePageGuard(BufferPoolManager *bpm, Page *page);
WritePageGuard(const WritePageGuard &) = delete;
auto operator=(const WritePageGuard &) -> WritePageGuard & = delete;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Move constructor for WritePageGuard
*
Expand All @@ -188,7 +188,7 @@ class WritePageGuard {
*/
WritePageGuard(WritePageGuard &&that) noexcept;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Move assignment for WritePageGuard
*
Expand All @@ -197,7 +197,7 @@ class WritePageGuard {
*/
auto operator=(WritePageGuard &&that) noexcept -> WritePageGuard &;

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Drop a WritePageGuard
*
Expand All @@ -208,7 +208,7 @@ class WritePageGuard {
*/
void Drop();

/** TODO(P2): Add implementation
/** TODO(P1): Add implementation
*
* @brief Destructor for WritePageGuard
*
Expand Down
4 changes: 4 additions & 0 deletions src/storage/page/page_guard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ auto BasicPageGuard::operator=(BasicPageGuard &&that) noexcept -> BasicPageGuard

BasicPageGuard::~BasicPageGuard(){}; // NOLINT

ReadPageGuard::ReadPageGuard(BufferPoolManager *bpm, Page *page) {}

ReadPageGuard::ReadPageGuard(ReadPageGuard &&that) noexcept = default;

auto ReadPageGuard::operator=(ReadPageGuard &&that) noexcept -> ReadPageGuard & { return *this; }
Expand All @@ -19,6 +21,8 @@ void ReadPageGuard::Drop() {}

ReadPageGuard::~ReadPageGuard() {} // NOLINT

WritePageGuard::WritePageGuard(BufferPoolManager *bpm, Page *page) {}

WritePageGuard::WritePageGuard(WritePageGuard &&that) noexcept = default;

auto WritePageGuard::operator=(WritePageGuard &&that) noexcept -> WritePageGuard & { return *this; }
Expand Down
2 changes: 0 additions & 2 deletions src/storage/table/table_heap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ auto TableHeap::InsertTuple(const TupleMeta &meta, const Tuple &tuple, LockManag

page_guard.Drop();

// acquire latch here as TSAN complains. Given we only have one insertion thread, this is fine.
npg->WLatch();
auto next_page_guard = WritePageGuard{bpm_, npg};

last_page_id_ = next_page_id;
Expand Down
55 changes: 55 additions & 0 deletions test/storage/cow_buffer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// BusTub
//
// page_guard_test.cpp
//
// Identification: test/storage/page_guard_test.cpp
//
// Copyright (c) 2015-2023, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "storage/disk/cow_buffer.h"
#include "gtest/gtest.h"

namespace bustub {

// NOLINTNEXTLINE
TEST(CoWBufferTest, ScheduleWriteReadPageTest) {
CoWBuffer cow{};
std::vector<Page *> cow_pages{};
for (size_t i{0}; i < 8; i++) {
Page bpm_page{};
auto content{"Meuh!: " + std::to_string(i) + " 🐄🐄🐄🐄"};
std::strncpy(bpm_page.GetData(), content.data(), BUSTUB_PAGE_SIZE);

Page *cow_page{cow.Add(&bpm_page)};
EXPECT_NE(cow_page, nullptr);
EXPECT_NE(cow_page, &bpm_page);
cow_pages.push_back(cow_page);
EXPECT_EQ(std::memcmp(cow_page->GetData(), bpm_page.GetData(), BUSTUB_PAGE_SIZE), 0);
}

Page extra_page{};
Page *cow_extra_page{cow.Add(&extra_page)};
EXPECT_EQ(cow_extra_page, nullptr);

for (size_t i{0}; i < 8; i++) {
auto check{"Meuh!: " + std::to_string(i) + " 🐄🐄🐄🐄"};
EXPECT_EQ(std::memcmp(cow_pages[i]->GetData(), check.data(), check.size()), 0);
}

cow.Remove(cow_pages[5]);
Page replace_page{};
cow_pages[5] = cow.Add(&replace_page);
EXPECT_NE(cow_pages[5], nullptr);

for (size_t i{0}; i < 8; i++) {
cow.Remove(cow_pages[i]); // Shouldn't crash.
}

cow.Remove(nullptr); // Shouldn't crash.
}

} // namespace bustub
Loading