Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into migrate-chunk-to-in…
Browse files Browse the repository at this point in the history
…clude-folder
  • Loading branch information
JacksonYao287 committed Sep 12, 2023
2 parents bf6004f + 91b264a commit 38d5399
Show file tree
Hide file tree
Showing 41 changed files with 975 additions and 150 deletions.
2 changes: 1 addition & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class HomestoreConan(ConanFile):
name = "homestore"
version = "4.2.0"
version = "4.2.1"

homepage = "https://github.com/eBay/Homestore"
description = "HomeStore Storage Engine"
Expand Down
8 changes: 7 additions & 1 deletion src/include/homestore/blkdata_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class BlkDataService {
*
* @param size : size of this vdev
*/
void create_vdev(uint64_t size);
void create_vdev(uint64_t size, homestore::blk_allocator_type_t alloc_type,
homestore::chunk_selector_type_t chunk_sel_type);

/**
* @brief : called during recovery to open existing vdev for data service
Expand Down Expand Up @@ -131,6 +132,11 @@ class BlkDataService {
*/
BlkReadTracker* read_blk_tracker() { return m_blk_read_tracker.get(); }

/**
* @brief : start data service;
*/
void start();

private:
BlkAllocStatus alloc_blks(uint32_t size, const blk_alloc_hints& hints, std::vector< BlkId >& out_blkids);

Expand Down
12 changes: 8 additions & 4 deletions src/include/homestore/homestore_decl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ ENUM(io_flag, uint8_t,
DIRECT_IO, // recommended mode
READ_ONLY // Read-only mode for post-mortem checks
);
ENUM(blk_allocator_type_t, uint8_t, none, fixed, varsize);
ENUM(blk_allocator_type_t, uint8_t, none, fixed, varsize, append);
ENUM(chunk_selector_type_t, uint8_t, // What are the options to select chunk to allocate a block
NONE, // Caller want nothing to be set
ROUND_ROBIN, // Pick round robin
HEAP, // Heap chunk selector
CUSTOM, // Controlled by the upper layer
RANDOM, // Pick any chunk in uniformly random fashion
MOST_AVAILABLE_SPACE, // Pick the most available space
Expand Down Expand Up @@ -165,6 +167,8 @@ struct HS_SERVICE {
struct hs_format_params {
float size_pct;
uint32_t num_chunks{1};
blk_allocator_type_t alloc_type{blk_allocator_type_t::varsize};
chunk_selector_type_t chunk_sel_type{chunk_selector_type_t::ROUND_ROBIN};
};

struct hs_input_params {
Expand Down Expand Up @@ -212,8 +216,8 @@ struct cap_attrs {
#endif

////////////// Misc ///////////////////
#define HOMESTORE_LOG_MODS \
btree_structures, btree_nodes, btree_generics, btree, cache, device, blkalloc, vol_io_wd, volume, flip, cp, metablk, \
indx_mgr, wbcache, logstore, replay, transient, IOMGR_LOG_MODS
#define HOMESTORE_LOG_MODS \
btree_structures, btree_nodes, btree_generics, btree, cache, device, blkalloc, vol_io_wd, volume, flip, cp, \
metablk, indx_mgr, wbcache, logstore, replay, transient, IOMGR_LOG_MODS

} // namespace homestore
4 changes: 3 additions & 1 deletion src/include/homestore/superblk_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class superblk {
return ++s_count;
}

superblk(const std::string& meta_name = "") {
superblk(const std::string& meta_name = "") { set_name(meta_name); }

void set_name(const std::string& meta_name) {
if (meta_name.empty()) {
m_metablk_name = "meta_blk_" + std::to_string(next_count());
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/blkalloc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ target_sources(hs_blkalloc PRIVATE
fixed_blk_allocator.cpp
varsize_blk_allocator.cpp
blk_cache_queue.cpp
append_blk_allocator.cpp
#blkalloc_cp.cpp
)
target_link_libraries(hs_blkalloc ${COMMON_DEPS})
add_dependencies(hs_blkalloc hs_common)
add_dependencies(hs_blkalloc hs_common)
192 changes: 192 additions & 0 deletions src/lib/blkalloc/append_blk_allocator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*********************************************************************************
* Modifications Copyright 2017-2019 eBay Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* *
* *********************************************************************************/
#include <homestore/checkpoint/cp_mgr.hpp>
#include <homestore/meta_service.hpp>

#include "append_blk_allocator.h"
#include "checkpoint/cp.hpp"

namespace homestore {

AppendBlkAllocator::AppendBlkAllocator(const BlkAllocConfig& cfg, bool need_format, chunk_num_t id) :
BlkAllocator{cfg, id}, m_metrics{get_name().c_str()} {
// TODO: try to make all append_blk_allocator instances use same client type to reduce metablk's cache footprint;
meta_service().register_handler(
get_name(),
[this](meta_blk* mblk, sisl::byte_view buf, size_t size) { on_meta_blk_found(std::move(buf), (void*)mblk); },
nullptr);

if (need_format) {
m_freeable_nblks = available_blks();
m_last_append_offset = 0;

for (uint8_t i = 0; i < m_sb.size(); ++i) {
m_sb[i].set_name(get_name());
m_sb[i].create(sizeof(append_blkalloc_ctx));
m_sb[i]->is_dirty = false;
m_sb[i]->allocator_id = id;
m_sb[i]->last_append_offset = 0;
m_sb[i]->freeable_nblks = m_freeable_nblks;
}
}

// for recovery boot, fields should be recovered from metablks;
}

void AppendBlkAllocator::on_meta_blk_found(const sisl::byte_view& buf, void* meta_cookie) {
// TODO: also needs to initialize base class blkallocator for recovery path;
// load all dirty buffer from the same starting point;
m_sb[0].load(buf, meta_cookie);
for (uint8_t i = 1; i < m_sb.size(); ++i) {
m_sb[i].load(buf, meta_cookie);
}

m_last_append_offset = m_sb[0]->last_append_offset;
m_freeable_nblks = m_sb[0]->freeable_nblks;

HS_REL_ASSERT_EQ(m_sb[0]->magic, append_blkalloc_sb_magic, "Invalid AppendBlkAlloc metablk, magic mismatch");
HS_REL_ASSERT_EQ(m_sb[0]->version, append_blkalloc_sb_version, "Invalid version of AppendBlkAllocator metablk");
}

//
// Every time buffer is being dirtied, it needs to be within CPGuard().
// It garunteens either this dirty buffer is flushed in current cp or next cp as a whole;
//
// alloc a single block;
//
BlkAllocStatus AppendBlkAllocator::alloc(BlkId& bid) {
std::unique_lock lk(m_mtx);
if (available_blks() < 1) {
COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1);
LOGERROR("No space left to serve request nblks: 1, available_blks: {}", available_blks());
return BlkAllocStatus::SPACE_FULL;
}

bid.set(m_last_append_offset, 1, m_chunk_id);

[[maybe_unused]] auto cur_cp = hs()->cp_mgr().cp_guard();
++m_last_append_offset;
--m_freeable_nblks;
set_dirty_offset(cur_cp->id() % MAX_CP_COUNT);

COUNTER_INCREMENT(m_metrics, num_alloc, 1);
return BlkAllocStatus::SUCCESS;
}

//
// For append blk allocator, the assumption is only one writer will append data on one chunk.
// If we want to change above design, we can open this api for vector allocation;
//
BlkAllocStatus AppendBlkAllocator::alloc(blk_count_t nblks, const blk_alloc_hints& hint,
std::vector< BlkId >& out_bids) {
std::unique_lock lk(m_mtx);
if (available_blks() < nblks) {
COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1);
LOGERROR("No space left to serve request nblks: {}, available_blks: {}", nblks, available_blks());
return BlkAllocStatus::SPACE_FULL;
} else if (nblks > BlkId::max_blks_in_op()) {
// consumer(vdev) already handles this case.
COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1);
LOGERROR("Can't serve request nblks: {} larger than max_blks_in_op: {}", nblks, BlkId::max_blks_in_op());
return BlkAllocStatus::FAILED;
}

// Push 1 blk to the vector which has all the requested nblks;
out_bids.emplace_back(m_last_append_offset, nblks, m_chunk_id);

[[maybe_unused]] auto cur_cp = hs()->cp_mgr().cp_guard();
m_last_append_offset += nblks;
m_freeable_nblks -= nblks;

// it is garunteened dirty buffer always contains updates of current_cp or next_cp, it will
// never get dirty buffer from across updates;
set_dirty_offset(cur_cp->id() % MAX_CP_COUNT);

COUNTER_INCREMENT(m_metrics, num_alloc, 1);

return BlkAllocStatus::SUCCESS;
}

//
// cp_flush doesn't need CPGuard as it is triggered by CPMgr which already handles the reference check;
//
// cp_flush should not block alloc/free;
//
void AppendBlkAllocator::cp_flush(CP* cp) {
const auto idx = cp->id();
// check if current cp's context has dirty buffer already
if (m_sb[idx]->is_dirty) {
// write to metablk;
m_sb[cp->id()].write();

// clear this dirty buff's dirty flag;
clear_dirty_offset(idx);
}
}

// updating current cp's dirty buffer context;
void AppendBlkAllocator::set_dirty_offset(const uint8_t idx) {
m_sb[idx]->is_dirty = true;

m_sb[idx]->last_append_offset = m_last_append_offset;
m_sb[idx]->freeable_nblks = m_freeable_nblks;
}

// clearing current cp context's dirty flag;
void AppendBlkAllocator::clear_dirty_offset(const uint8_t idx) { m_sb[idx]->is_dirty = false; }

//
// free operation does:
// 1. book keeping "total freeable" space
// 2. if the blk being freed happens to be last block, move last_append_offset backwards accordingly;
//
void AppendBlkAllocator::free(const BlkId& bid) {
std::unique_lock lk(m_mtx);
[[maybe_unused]] auto cur_cp = hs()->cp_mgr().cp_guard();
const auto n = bid.get_nblks();
m_freeable_nblks += n;
if (bid.get_blk_num() + n == m_last_append_offset) {
// we are freeing the the last blk id, let's rewind.
m_last_append_offset -= n;
}
set_dirty_offset(cur_cp->id() % MAX_CP_COUNT);
}

void AppendBlkAllocator::free(const std::vector< BlkId >& blk_ids) {
for (const auto b : blk_ids) {
this->free(b);
}
}

blk_cap_t AppendBlkAllocator::available_blks() const { return get_total_blks() - get_used_blks(); }

blk_cap_t AppendBlkAllocator::get_used_blks() const { return m_last_append_offset; }

bool AppendBlkAllocator::is_blk_alloced(const BlkId& in_bid, bool) const {
// blk_num starts from 0;
return in_bid.get_blk_num() < get_used_blks();
}

std::string AppendBlkAllocator::get_name() const { return "AppendBlkAlloc_chunk_" + std::to_string(m_chunk_id); }

std::string AppendBlkAllocator::to_string() const {
auto str = fmt::format("{}, last_append_offset: {}", get_name(), m_last_append_offset);
return str;
}

blk_cap_t AppendBlkAllocator::get_freeable_nblks() const { return m_freeable_nblks; }

} // namespace homestore
112 changes: 112 additions & 0 deletions src/lib/blkalloc/append_blk_allocator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*********************************************************************************
* Modifications Copyright 2017-2019 eBay Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* *
* *********************************************************************************/
#pragma once

#include <sisl/logging/logging.h>
#include "blk_allocator.h"
#include "common/homestore_assert.hpp"
#include "common/homestore_config.hpp"
#include <homestore/blk.h>
#include <homestore/checkpoint/cp_mgr.hpp>
#include <homestore/homestore.hpp>
#include <homestore/superblk_handler.hpp>

namespace homestore {
static constexpr uint64_t append_blkalloc_sb_magic{0xd0d0d02b};
static constexpr uint64_t append_blkalloc_sb_version{0x1};

#pragma pack(1)
struct append_blkalloc_ctx {
uint64_t magic{append_blkalloc_sb_magic};
uint32_t version{append_blkalloc_sb_version};
bool is_dirty; // this field is needed for cp_flush, but not necessarily needed for persistence;
uint64_t allocator_id;
uint64_t freeable_nblks;
uint64_t last_append_offset;
};
#pragma pack()

class AppendBlkAllocMetrics : public sisl::MetricsGroup {
public:
explicit AppendBlkAllocMetrics(const char* inst_name) : sisl::MetricsGroup("AppendBlkAlloc", inst_name) {
REGISTER_COUNTER(num_alloc, "Number of blks alloc attempts");
REGISTER_COUNTER(num_alloc_failure, "Number of blk alloc failures");

register_me_to_farm();
}

AppendBlkAllocMetrics(const AppendBlkAllocMetrics&) = delete;
AppendBlkAllocMetrics(AppendBlkAllocMetrics&&) noexcept = delete;
AppendBlkAllocMetrics& operator=(const AppendBlkAllocMetrics&) = delete;
AppendBlkAllocMetrics& operator=(AppendBlkAllocMetrics&&) noexcept = delete;
~AppendBlkAllocMetrics() { deregister_me_from_farm(); }
};

//
// The assumption for AppendBlkAllocator:
// 1. Operations (alloc/free) are being called multiple threadeds
// 2. cp_flush will be triggered in a different thread
//
// Why do we want thread-safe AppendBlkAllocator:
// 1. one reason is it makes sense for AppendBlkAllocator to work on a nvme drive
// 2. for HDD, performance will drop significantly if alloc/write is being done in multi-threaded model, it is left for
// consumer to make choice;
//
class AppendBlkAllocator : public BlkAllocator {
public:
AppendBlkAllocator(const BlkAllocConfig& cfg, bool need_format, chunk_num_t id = 0);

AppendBlkAllocator(const AppendBlkAllocator&) = delete;
AppendBlkAllocator(AppendBlkAllocator&&) noexcept = delete;
AppendBlkAllocator& operator=(const AppendBlkAllocator&) = delete;
AppendBlkAllocator& operator=(AppendBlkAllocator&&) noexcept = delete;
virtual ~AppendBlkAllocator() = default;

BlkAllocStatus alloc(BlkId& bid) override;
BlkAllocStatus alloc(blk_count_t nblks, const blk_alloc_hints& hints, std::vector< BlkId >& out_blkid) override;

void free(const std::vector< BlkId >& blk_ids) override;
void free(const BlkId& b) override;

blk_cap_t available_blks() const override;
blk_cap_t get_used_blks() const override;
blk_cap_t get_freeable_nblks() const;

bool is_blk_alloced(const BlkId& in_bid, bool use_lock = false) const override;
std::string to_string() const override;

/// @brief : needs to be called with cp_guard();
void set_dirty_offset(const uint8_t idx);

/// @brief : clear dirty is best effort;
/// offset flush is idempotent;
void clear_dirty_offset(const uint8_t idx);

void cp_flush(CP* cp) override;

private:
std::string get_name() const;
void on_meta_blk_found(const sisl::byte_view& buf, void* meta_cookie);

private:
std::mutex m_mtx; // thread_safe, TODO: open option for consumer to choose to go lockless;
uint64_t m_last_append_offset{0}; // last appended offset in blocks;
uint64_t m_freeable_nblks{0};
AppendBlkAllocMetrics m_metrics;
std::array< superblk< append_blkalloc_ctx >, MAX_CP_COUNT > m_sb;
};

} // namespace homestore
Loading

0 comments on commit 38d5399

Please sign in to comment.