Skip to content
Closed
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
32 changes: 28 additions & 4 deletions src/overlaybd/lsmt/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,29 @@ static int is_zero_block(char *buf, size_t n) {
return 0;
}

// Offsets and the return value are in 512-byte sectors.
static ssize_t pad_compact_data(IFile *dest, uint64_t moffset) {
const uint64_t sectors_per_page = ALIGNMENT4K / ALIGNMENT;
auto padding = (sectors_per_page - moffset % sectors_per_page) % sectors_per_page;
if (padding) {
ALIGNED_MEM4K(zeros, ALIGNMENT4K);
memset(zeros, 0, ALIGNMENT4K);
auto bytes = padding * ALIGNMENT;
if (dest->write(zeros, bytes) != (ssize_t)bytes)
LOG_ERRNO_RETURN(0, -1, "failed to write compact padding");
}
return padding;
}

static ssize_t pcopy(const CompactOptions &opt, const SegmentMapping &m, uint64_t moffset,
vector<SegmentMapping> &index) {
auto padding = pad_compact_data(opt.commit_args->as, moffset);
if (padding < 0)
return -1;
moffset += padding;
auto offset = m.moffset * ALIGNMENT;
auto count = m.length * ALIGNMENT;
auto bytes = 0;
auto bytes = padding * ALIGNMENT;
const size_t BUFFER_SIZE = 32 * 1024;
ALIGNED_MEM4K(buf, BUFFER_SIZE);
ALIGNED_MEM4K(data, BUFFER_SIZE);
Expand Down Expand Up @@ -462,7 +480,10 @@ static int compact(const CompactOptions &opt, atomic_uint64_t &compacted_idx_siz
return (int)ret;
moffset += ret;
}
uint64_t index_offset = moffset * ALIGNMENT;
auto padding_sectors = pad_compact_data(dest_file, moffset);
if (padding_sectors < 0)
return -1;
uint64_t index_offset = (moffset + padding_sectors) * ALIGNMENT;
auto index_size = compress_raw_index(&compact_index[0], compact_index.size());
LOG_DEBUG("write index to dest_file `, size: `*`", dest_file, index_size,
sizeof(SegmentMapping));
Expand Down Expand Up @@ -1549,7 +1570,7 @@ class LSMTWarpFile : public LSMTFile {
return nwrite;
}

size_t compact(CompactOptions &opts, size_t moffset, size_t &nindex) const {
ssize_t compact(CompactOptions &opts, size_t moffset, size_t &nindex) const {

auto dest_file = opts.commit_args->as;
auto marray = ptr_array(&opts.raw_index[0], opts.index_size);
Expand All @@ -1574,7 +1595,10 @@ class LSMTWarpFile : public LSMTFile {
return (int)ret;
moffset += ret;
}
uint64_t index_offset = moffset * ALIGNMENT;
auto padding_sectors = pad_compact_data(dest_file, moffset);
if (padding_sectors < 0)
return -1;
uint64_t index_offset = (moffset + padding_sectors) * ALIGNMENT;
auto index_size = compress_raw_index(&compact_index[0], compact_index.size());
LOG_DEBUG("write index to dest_file `, offset: `, size: `*`", dest_file, index_offset,
index_size, sizeof(SegmentMapping));
Expand Down
7 changes: 7 additions & 0 deletions src/overlaybd/lsmt/format_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ supposed to be referenced by the index entries. And it's possible for this secti
to contain garbage data blocks that are not referenced by the any entry of the
index.

Compaction aligns the file offset of each non-zero local data extent to 4096
bytes, and aligns the index start to 4096 bytes after the data. Padding is
zero-filled and adds at most 3584 bytes before each extent. Explicit zero
extents remain index-only, and remote mappings keep their original offsets.
The sector units and wire format are unchanged; mapping offsets and lengths
are still expressed in 512-byte sectors.


## index
The index section is a table that associates logical block addressing (LBA) with
Expand Down
80 changes: 80 additions & 0 deletions src/overlaybd/lsmt/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,77 @@ TEST_F(FileTest, reject_oversized_rw_index) {

void lookup_test(IMemoryIndex &idx);

static void check_compact_layout(IFileRW *rw, IFile *dest, bool warp) {
SegmentMapping extents[] = {{1, 1, 0}, {8, 1, 0}, {12, 3, 0}, {17, 80, 0}};
extents[2].discard();
vector<char> expected(104 * ALIGNMENT, 0);
for (size_t i = 0; i < LEN(extents); ++i) {
auto &m = extents[i];
auto size = m.length * ALIGNMENT;
auto offset = m.offset * ALIGNMENT;
if (m.zeroed) {
ASSERT_EQ(0, rw->fallocate(3, offset, size));
} else {
memset(expected.data() + offset, 0x31 + i, size);
ASSERT_EQ((ssize_t)size, rw->pwrite(expected.data() + offset, size, offset));
}
}
CommitArgs args(dest);
ASSERT_EQ(0, rw->commit(args));
ALIGNED_MEM4K(trailer, HeaderTrailer::SPACE);
auto end = dest->lseek(0, SEEK_END);
ASSERT_EQ((ssize_t)HeaderTrailer::SPACE,
dest->pread(trailer, HeaderTrailer::SPACE, end - HeaderTrailer::SPACE));
auto ht = (HeaderTrailer *)trailer;
vector<SegmentMapping> index(ht->index_size);
auto bytes = index.size() * sizeof(SegmentMapping);
ASSERT_EQ((ssize_t)bytes, dest->pread(index.data(), bytes, ht->index_offset));
uint64_t cursor = HeaderTrailer::SPACE;
vector<char> payload;
size_t n = 0;
for (auto &m : index) {
if (m.offset == Segment::INVALID_OFFSET)
continue;
ASSERT_LT(n, LEN(extents));
EXPECT_EQ(extents[n].offset + 0, m.offset + 0);
EXPECT_EQ(extents[n].length + 0, m.length + 0);
EXPECT_EQ(extents[n].zeroed + 0, m.zeroed + 0);
if (!m.zeroed)
cursor = (cursor + ALIGNMENT4K - 1) / ALIGNMENT4K * ALIGNMENT4K;
EXPECT_EQ(cursor, m.moffset * ALIGNMENT);
if (!m.zeroed) {
payload.resize(cursor - HeaderTrailer::SPACE + m.length * ALIGNMENT, 0);
memcpy(payload.data() + cursor - HeaderTrailer::SPACE,
expected.data() + m.offset * ALIGNMENT, m.length * ALIGNMENT);
cursor += m.length * ALIGNMENT;
}
++n;
}
EXPECT_EQ(LEN(extents), n);
cursor = (cursor + ALIGNMENT4K - 1) / ALIGNMENT4K * ALIGNMENT4K;
EXPECT_EQ(cursor, ht->index_offset + 0);
payload.resize(cursor - HeaderTrailer::SPACE, 0);
vector<char> actual(payload.size());
ASSERT_EQ((ssize_t)actual.size(), dest->pread(actual.data(), actual.size(), HeaderTrailer::SPACE));
EXPECT_EQ(payload, actual);
// There are no remote extents; the warp reader still requires a target.
unique_ptr<IFileRO> ro(warp ? open_warpfile_ro(dest, dest, false)
: LSMT::open_file_ro(dest, false));
ASSERT_NE(nullptr, ro);
actual.resize(expected.size());
ASSERT_EQ((ssize_t)actual.size(), ro->pread(actual.data(), actual.size(), 0));
EXPECT_EQ(expected, actual);
}

TEST_F(FileTest, compact_layout_4k) {
unique_ptr<IFileRW> rw(create_file_rw());
unique_ptr<IFile> dest(lfs->open(layer_name.back().c_str(),
O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
ASSERT_NE(nullptr, rw);
ASSERT_NE(nullptr, dest);
check_compact_layout(rw.get(), dest.get(), false);
}

TEST(Index, lookup) {
const static SegmentMapping mapping[] = {{0, 10, 0}, {10, 10, 50}, {100, 10, 20}};
Index idx(mapping, LEN(mapping), false);
Expand Down Expand Up @@ -1921,6 +1992,15 @@ TEST_F(WarpFileTest, commit) {
delete file;
}

TEST_F(WarpFileTest, compact_layout_4k) {
unique_ptr<IFileRW> rw(create_warpfile_rw(ut_io_engine));
unique_ptr<IFile> dest(lfs->open(layer_name.back().c_str(),
O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
ASSERT_NE(nullptr, rw);
ASSERT_NE(nullptr, dest);
check_compact_layout(rw.get(), dest.get(), true);
}

TEST_F(WarpFileTest, large_lba) {
CleanUp();
log_output_level = FLAGS_log_level;
Expand Down
Loading