Skip to content

Commit

Permalink
feat: add CoW buffer for spring2024 p1 (#690)
Browse files Browse the repository at this point in the history
* Prepare Spring 24 P1

* fix typo

* add default impl for page guard constructor

* format

* new line at eof

---------

Co-authored-by: xx01cyx <[email protected]>
  • Loading branch information
AlSchlo and xx01cyx committed Jan 27, 2024
1 parent 258d8e2 commit 400d57c
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 17 deletions.
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

0 comments on commit 400d57c

Please sign in to comment.