Skip to content

Commit e244d81

Browse files
committed
Implement homeblock write path.
Write flow writes data to disk, add key value to index and flush journal entry to journal. Add test case to test the normal io flow.
1 parent db2ae04 commit e244d81

File tree

13 files changed

+833
-94
lines changed

13 files changed

+833
-94
lines changed

conanfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class HomeBlocksConan(ConanFile):
1111
name = "homeblocks"
12-
version = "0.0.15"
12+
version = "0.0.16"
1313
homepage = "https://github.com/eBay/HomeBlocks"
1414
description = "Block Store built on HomeStore"
1515
topics = ("ebay")

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 3.11)
22

33
add_flags("-Wall -Wextra -Werror -Wpedantic")
44
include_directories (BEFORE include/)
5+
include_directories (BEFORE lib)
56
include_directories (AFTER .)
67

78
find_package(Threads QUIET REQUIRED)

src/include/homeblks/volume_mgr.hpp

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
namespace homeblocks {
1212

1313
ENUM(VolumeError, uint16_t, UNKNOWN = 1, INVALID_ARG, TIMEOUT, UNKNOWN_VOLUME, UNSUPPORTED_OP, CRC_MISMATCH,
14-
NO_SPACE_LEFT, DRIVE_WRITE_ERROR, INTERNAL_ERROR);
14+
NO_SPACE_LEFT, DRIVE_WRITE_ERROR, INTERNAL_ERROR, INDEX_ERROR);
15+
16+
using lba_t = uint64_t;
17+
using lba_count_t = uint32_t;
1518

16-
using lba_t = uint64_t;
17-
using lba_count_t = uint32_t;
18-
1919
struct vol_interface_req : public sisl::ObjLifeCounter< vol_interface_req > {
2020
uint8_t* buffer{nullptr};
2121
lba_t lba;
@@ -62,7 +62,7 @@ struct VolumeInfo {
6262

6363
std::string to_string() {
6464
return fmt::format("VolumeInfo: id={} size_bytes={}, page_size={}, name={}", boost::uuids::to_string(id),
65-
size_bytes, page_size, name);
65+
size_bytes, page_size, name);
6666
}
6767
};
6868

@@ -102,36 +102,36 @@ class VolumeManager : public Manager< VolumeError > {
102102
* @return std::error_condition no_error or error in issuing writes
103103
*/
104104
virtual NullAsyncResult write(const VolumePtr& vol, const vol_interface_req_ptr& req,
105-
bool part_of_batch = false) = 0;
105+
bool part_of_batch = false) = 0;
106106

107107
/**
108-
* @brief Read the data from the volume asynchronously, created from the request. After completion the attached
109-
* callback function will be called with this req ptr.
110-
*
111-
* @param vol Pointer to the volume
112-
* @param req Request created which contains all the read parameters
113-
* @param part_of_batch Is this request part of a batch request. If so, implementation can wait for batch_submit
114-
* call before issuing the reads. IO might already be started or even completed (in case of errors) before
115-
* batch_sumbit call, so application cannot assume IO will be started only after submit_batch call.
116-
*
117-
* @return std::error_condition no_error or error in issuing reads
118-
*/
108+
* @brief Read the data from the volume asynchronously, created from the request. After completion the attached
109+
* callback function will be called with this req ptr.
110+
*
111+
* @param vol Pointer to the volume
112+
* @param req Request created which contains all the read parameters
113+
* @param part_of_batch Is this request part of a batch request. If so, implementation can wait for batch_submit
114+
* call before issuing the reads. IO might already be started or even completed (in case of errors) before
115+
* batch_sumbit call, so application cannot assume IO will be started only after submit_batch call.
116+
*
117+
* @return std::error_condition no_error or error in issuing reads
118+
*/
119119
virtual NullAsyncResult read(const VolumePtr& vol, const vol_interface_req_ptr& req,
120-
bool part_of_batch = false) = 0;
120+
bool part_of_batch = false) = 0;
121121

122122
/**
123-
* @brief unmap the given block range
124-
*
125-
* @param vol Pointer to the volume
126-
* @param req Request created which contains all the read parameters
127-
*/
123+
* @brief unmap the given block range
124+
*
125+
* @param vol Pointer to the volume
126+
* @param req Request created which contains all the read parameters
127+
*/
128128
virtual NullAsyncResult unmap(const VolumePtr& vol, const vol_interface_req_ptr& req) = 0;
129129

130130
/**
131-
* @brief Submit the io batch, which is a mandatory method to be called if read/write are issued with part_of_batch
132-
* is set to true. In those cases, without this method, IOs might not be even issued. No-op if previous io requests
133-
* are not part of batch.
134-
*/
131+
* @brief Submit the io batch, which is a mandatory method to be called if read/write are issued with part_of_batch
132+
* is set to true. In those cases, without this method, IOs might not be even issued. No-op if previous io requests
133+
* are not part of batch.
134+
*/
135135
virtual void submit_io_batch() = 0;
136136

137137
/**

src/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ target_sources(${PROJECT_NAME}_core PRIVATE
77
homeblks_impl.cpp
88
volume_mgr.cpp
99
listener.cpp
10+
index.cpp
1011
common.cpp
1112
)
1213
target_link_libraries(${PROJECT_NAME}_core

src/lib/homeblks_impl.hpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <map>
1919
#include <string>
2020
#include <sisl/logging/logging.h>
21+
#include <sisl/utility/obj_life_counter.hpp>
22+
#include <homestore/crc.h>
2123
#include <homestore/homestore.hpp>
2224
#include <homestore/index/index_table.hpp>
2325
#include <homestore/superblk_handler.hpp>
@@ -30,6 +32,14 @@ namespace homeblocks {
3032

3133
class Volume;
3234

35+
ENUM(HomeBlksMsgType, uint8_t, READ, WRITE, UNMAP);
36+
37+
struct VolJournalEntry {
38+
lba_t start_lba;
39+
lba_count_t nlbas;
40+
uint16_t num_old_blks;
41+
};
42+
3343
class HomeBlocksImpl : public HomeBlocks, public VolumeManager, public std::enable_shared_from_this< HomeBlocksImpl > {
3444
struct homeblks_sb_t {
3545
uint64_t magic;
@@ -119,6 +129,9 @@ class HomeBlocksImpl : public HomeBlocks, public VolumeManager, public std::enab
119129

120130
void on_init_complete();
121131

132+
void on_write(int64_t lsn, const sisl::blob& header, const sisl::blob& key,
133+
const std::vector< homestore::MultiBlkId >& blkids, cintrusive< homestore::repl_req_ctx >& ctx);
134+
122135
private:
123136
// Should only be called for first-time-boot
124137
void superblk_init();
@@ -133,6 +146,10 @@ class HomeBlocksImpl : public HomeBlocks, public VolumeManager, public std::enab
133146
// recovery apis
134147
void on_hb_meta_blk_found(sisl::byte_view const& buf, void* cookie);
135148
void on_vol_meta_blk_found(sisl::byte_view const& buf, void* cookie);
149+
150+
VolumeManager::Result< folly::Unit > write_to_index(const VolumePtr& vol_ptr, const vol_interface_req_ptr& vol_req,
151+
lba_t start_lba, lba_t end_lba,
152+
std::unordered_map< lba_t, BlockInfo >& blocks_info);
136153
};
137154

138155
class HBIndexSvcCB : public homestore::IndexServiceCallbacks {
@@ -147,4 +164,87 @@ class HBIndexSvcCB : public homestore::IndexServiceCallbacks {
147164
private:
148165
HomeBlocksImpl* hb_;
149166
};
167+
168+
const homestore::csum_t init_crc_16 = 0x8005;
169+
170+
struct HomeBlksMessageHeader {
171+
HomeBlksMessageHeader() = default;
172+
HomeBlksMsgType msg_type;
173+
volume_id_t volume_id;
174+
175+
std::string to_string() const {
176+
return fmt::format(" msg_type={}volume={}\n", enum_name(msg_type), boost::uuids::to_string(volume_id));
177+
}
178+
};
179+
180+
struct homeblks_repl_ctx : public homestore::repl_req_ctx {
181+
sisl::io_blob_safe hdr_buf_;
182+
sisl::io_blob_safe key_buf_;
183+
184+
// Data bufs corresponding to data_sgs_. Since data_sgs are raw pointers, we need to keep the data bufs alive
185+
folly::small_vector< sisl::io_blob_safe, 3 > data_bufs_;
186+
sisl::sg_list data_sgs_;
187+
188+
homeblks_repl_ctx(uint32_t hdr_extn_size, uint32_t key_size = 0) : homestore::repl_req_ctx{} {
189+
hdr_buf_ = std::move(sisl::io_blob_safe{uint32_cast(sizeof(HomeBlksMessageHeader) + hdr_extn_size), 0});
190+
new (hdr_buf_.bytes()) HomeBlksMessageHeader();
191+
192+
if (key_size) { key_buf_ = std::move(sisl::io_blob_safe{key_size, 0}); }
193+
data_sgs_.size = 0;
194+
}
195+
196+
~homeblks_repl_ctx() {
197+
if (hdr_buf_.bytes()) { header()->~HomeBlksMessageHeader(); }
198+
}
199+
200+
template < typename T >
201+
T* to() {
202+
return r_cast< T* >(this);
203+
}
204+
205+
HomeBlksMessageHeader* header() { return r_cast< HomeBlksMessageHeader* >(hdr_buf_.bytes()); }
206+
uint8_t* header_extn() { return hdr_buf_.bytes() + sizeof(HomeBlksMessageHeader); }
207+
208+
sisl::io_blob_safe& header_buf() { return hdr_buf_; }
209+
sisl::io_blob_safe const& cheader_buf() const { return hdr_buf_; }
210+
211+
sisl::io_blob_safe& key_buf() { return key_buf_; }
212+
sisl::io_blob_safe const& ckey_buf() const { return key_buf_; }
213+
214+
void add_data_sg(uint8_t* buf, uint32_t size) {
215+
data_sgs_.iovs.emplace_back(iovec{.iov_base = buf, .iov_len = size});
216+
data_sgs_.size += size;
217+
}
218+
219+
void add_data_sg(sisl::io_blob_safe&& buf) {
220+
add_data_sg(buf.bytes(), buf.size());
221+
data_bufs_.emplace_back(std::move(buf));
222+
}
223+
224+
sisl::sg_list& data_sgs() { return data_sgs_; }
225+
std::string data_sgs_string() const {
226+
fmt::memory_buffer buf;
227+
fmt::format_to(fmt::appender(buf), "total_size={} iovcnt={} [", data_sgs_.size, data_sgs_.iovs.size());
228+
for (auto const& iov : data_sgs_.iovs) {
229+
fmt::format_to(fmt::appender(buf), "<base={},len={}> ", iov.iov_base, iov.iov_len);
230+
}
231+
fmt::format_to(fmt::appender(buf), "]");
232+
return fmt::to_string(buf);
233+
}
234+
};
235+
236+
template < typename T >
237+
struct repl_result_ctx : public homeblks_repl_ctx {
238+
folly::Promise< T > promise_;
239+
VolumePtr vol_ptr_{nullptr};
240+
241+
template < typename... Args >
242+
static intrusive< repl_result_ctx< T > > make(Args&&... args) {
243+
return intrusive< repl_result_ctx< T > >{new repl_result_ctx< T >(std::forward< Args >(args)...)};
244+
}
245+
246+
repl_result_ctx(uint32_t hdr_extn_size, uint32_t key_size = 0) : homeblks_repl_ctx{hdr_extn_size, key_size} {}
247+
folly::SemiFuture< T > result() { return promise_.getSemiFuture(); }
248+
};
249+
150250
} // namespace homeblocks

src/lib/index.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*********************************************************************************
2+
* Modifications Copyright 2017-2019 eBay Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed
10+
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11+
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
12+
* specific language governing permissions and limitations under the License.
13+
*
14+
*********************************************************************************/
15+
#include "volume/volume.hpp"
16+
#include "homeblks_impl.hpp"
17+
18+
namespace homeblocks {
19+
20+
VolumeManager::Result< folly::Unit >
21+
HomeBlocksImpl::write_to_index(const VolumePtr& vol_ptr, const vol_interface_req_ptr& vol_req, lba_t start_lba,
22+
lba_t end_lba, std::unordered_map< lba_t, BlockInfo >& blocks_info) {
23+
24+
// Use filter callback to get the old blkid.
25+
homestore::put_filter_cb_t filter_cb = [&blocks_info](BtreeKey const& key, BtreeValue const& existing_value,
26+
BtreeValue const& value) {
27+
auto lba = r_cast< const VolumeIndexKey& >(key).key();
28+
blocks_info[lba].old_blkid = r_cast< const VolumeIndexValue& >(existing_value).blkid();
29+
return homestore::put_filter_decision::replace;
30+
};
31+
32+
// Write to prefix btree with key ranging from start_lba to end_lba.
33+
// For value shift() will get the blk_num and checksum for each lba.
34+
IndexValueContext app_ctx{&blocks_info, start_lba};
35+
const BlkId& start_blkid = blocks_info[start_lba].new_blkid;
36+
VolumeIndexValue value{start_blkid};
37+
38+
auto req = homestore::BtreeRangePutRequest< VolumeIndexKey >{
39+
homestore::BtreeKeyRange< VolumeIndexKey >{VolumeIndexKey{start_lba}, true, VolumeIndexKey{end_lba}, true},
40+
homestore::btree_put_type::UPSERT,
41+
r_cast< VolumeIndexValue* >(&value),
42+
r_cast< void* >(&app_ctx),
43+
std::numeric_limits< uint32_t >::max(),
44+
filter_cb};
45+
auto result = vol_ptr->indx_table()->put(req);
46+
if (result != homestore::btree_status_t::success) {
47+
LOGERROR("Failed to put to index range=({},{}) error={}", start_lba, end_lba, result);
48+
return folly::makeUnexpected(VolumeError::INDEX_ERROR);
49+
}
50+
51+
return folly::Unit();
52+
}
53+
54+
} // namespace homeblocks

src/lib/listener.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,22 @@ namespace homeblocks {
1919

2020
void HBListener::on_commit(int64_t lsn, sisl::blob const& header, sisl::blob const& key,
2121
std::vector< homestore::MultiBlkId > const& blkids,
22-
cintrusive< homestore::repl_req_ctx >& ctx) {}
22+
cintrusive< homestore::repl_req_ctx >& ctx) {
23+
// on_commit called whenever journal has flushed log entries. header contains the msg type and volume
24+
// id, key contains the list of checksum, list of old blkids for write case. blkid's are the new blkid's
25+
// where data is written.
26+
const HomeBlksMessageHeader* msg_header = r_cast< const HomeBlksMessageHeader* >(header.cbytes());
27+
switch (msg_header->msg_type) {
28+
case HomeBlksMsgType::WRITE:
29+
hb_->on_write(lsn, header, key, blkids, ctx);
30+
break;
31+
32+
case HomeBlksMsgType::READ:
33+
break;
34+
case HomeBlksMsgType::UNMAP:
35+
break;
36+
}
37+
}
2338

2439
bool HBListener::on_pre_commit(int64_t lsn, const sisl::blob& header, const sisl::blob& key,
2540
cintrusive< homestore::repl_req_ctx >& ctx) {

0 commit comments

Comments
 (0)