Skip to content

Commit 2abe2c0

Browse files
authored
fix: ExternalAllocator::Free with large sizes (#4388)
ExternalAllocator allocates large sizes directly from the extent tree bypassing segment data structure. Unfortunately, we forgot to align Free() the same way. This PR: 1. Make sure that we add back the allocated range to the extent tree in Free. 2. Rewrite and simplify ExtentTree::Add implementation. Signed-off-by: Roman Gershman <[email protected]>
1 parent 5e7acbb commit 2abe2c0

File tree

4 files changed

+41
-35
lines changed

4 files changed

+41
-35
lines changed

src/core/extent_tree.cc

+21-29
Original file line numberDiff line numberDiff line change
@@ -15,47 +15,39 @@ void ExtentTree::Add(size_t start, size_t len) {
1515
DCHECK_GT(len, 0u);
1616
DCHECK_EQ(len_extents_.size(), extents_.size());
1717

18-
size_t end = start + len;
19-
20-
if (extents_.empty()) {
21-
extents_.emplace(start, end);
22-
len_extents_.emplace(len, start);
23-
24-
return;
25-
}
26-
2718
auto it = extents_.lower_bound(start);
28-
bool merged = false;
19+
optional<absl::btree_map<size_t, size_t>::iterator> prev_extent;
2920

3021
if (it != extents_.begin()) {
3122
auto prev = it;
3223
--prev;
3324

3425
DCHECK_LE(prev->second, start);
35-
if (prev->second == start) { // [first, second = start, end)
36-
merged = true;
37-
len_extents_.erase(pair{prev->second - prev->first, prev->first});
38-
39-
// check if we join: prev, [start, end), [it->first, it->second]
40-
if (it != extents_.end() && end == it->first) { // [first, end = it->first, it->second)
41-
prev->second = it->second;
42-
len_extents_.erase(pair{it->second - it->first, it->first});
43-
extents_.erase(it);
44-
} else {
45-
prev->second = end; // just extend prev
46-
}
47-
len_extents_.emplace(prev->second - prev->first, prev->first);
26+
if (prev->second == start) { // combine with the previous extent
27+
size_t prev_len = prev->second - prev->first;
28+
CHECK_EQ(1u, len_extents_.erase(pair{prev_len, prev->first}));
29+
prev->second += len;
30+
start = prev->first;
31+
len += prev_len;
32+
prev_extent = prev;
4833
}
4934
}
5035

51-
if (!merged) {
52-
if (end == it->first) { // [start, end), [it->first, it->second]
53-
len_extents_.erase(pair{it->second - it->first, it->first});
54-
end = it->second;
36+
if (it != extents_.end()) {
37+
DCHECK_GE(it->first, start + len);
38+
if (start + len == it->first) { // merge with the next extent
39+
size_t it_len = it->second - it->first;
40+
CHECK_EQ(1u, len_extents_.erase(pair{it_len, it->first}));
5541
extents_.erase(it);
42+
len += it_len;
5643
}
57-
extents_.emplace(start, end);
58-
len_extents_.emplace(end - start, start);
44+
}
45+
46+
len_extents_.emplace(len, start);
47+
if (prev_extent) {
48+
(*prev_extent)->second = start + len;
49+
} else {
50+
extents_.emplace(start, start + len);
5951
}
6052
}
6153

src/core/extent_tree_test.cc

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,28 @@ TEST_F(ExtentTreeTest, Basic) {
2828
tree_.Add(0, 256);
2929
auto op = tree_.GetRange(64, 16);
3030
EXPECT_TRUE(op);
31-
EXPECT_THAT(*op, testing::Pair(0, 64));
31+
EXPECT_THAT(*op, testing::Pair(0, 64)); // [64, 256)
3232

3333
tree_.Add(56, 8);
3434
op = tree_.GetRange(64, 16);
3535
EXPECT_TRUE(op);
36-
EXPECT_THAT(*op, testing::Pair(64, 128));
36+
EXPECT_THAT(*op, testing::Pair(64, 128)); // {[56, 64), [128, 256)}
3737

3838
op = tree_.GetRange(18, 2);
3939
EXPECT_TRUE(op);
40-
EXPECT_THAT(*op, testing::Pair(128, 146));
40+
EXPECT_THAT(*op, testing::Pair(128, 146)); // {[56, 64), [146, 256)}
4141

4242
op = tree_.GetRange(80, 16);
4343
EXPECT_TRUE(op);
44-
EXPECT_THAT(*op, testing::Pair(160, 240));
44+
EXPECT_THAT(*op, testing::Pair(160, 240)); // {[56, 64), [146, 160), [240, 256)}
4545

4646
op = tree_.GetRange(4, 1);
4747
EXPECT_TRUE(op);
48-
EXPECT_THAT(*op, testing::Pair(56, 60));
48+
EXPECT_THAT(*op, testing::Pair(56, 60)); // {[60, 64), [146, 160), [240, 256)}
4949

5050
op = tree_.GetRange(32, 1);
5151
EXPECT_FALSE(op);
52-
tree_.Add(64, 240 - 64);
52+
tree_.Add(64, 146 - 64);
5353
op = tree_.GetRange(32, 4);
5454
EXPECT_TRUE(op);
5555
EXPECT_THAT(*op, testing::Pair(60, 92));

src/server/tiering/external_alloc.cc

+6
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,12 @@ int64_t ExternalAllocator::Malloc(size_t sz) {
367367
}
368368

369369
void ExternalAllocator::Free(size_t offset, size_t sz) {
370+
if (sz > kMediumObjMaxSize) {
371+
size_t align_sz = alignup(sz, 4_KB);
372+
extent_tree_.Add(offset, align_sz);
373+
return;
374+
}
375+
370376
size_t idx = offset / 256_MB;
371377
size_t delta = offset % 256_MB;
372378
CHECK_LT(idx, segments_.size());

src/server/tiering/external_alloc_test.cc

+8
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,12 @@ TEST_F(ExternalAllocatorTest, EmptyFull) {
146146
EXPECT_GT(ext_alloc_.Malloc(kAllocSize), 0u);
147147
}
148148

149+
TEST_F(ExternalAllocatorTest, AllocLarge) {
150+
ext_alloc_.AddStorage(0, kSegSize);
151+
152+
off_t offs = ext_alloc_.Malloc(2_MB - 1);
153+
EXPECT_EQ(offs, 0);
154+
ext_alloc_.Free(offs, 2_MB - 1);
155+
}
156+
149157
} // namespace dfly::tiering

0 commit comments

Comments
 (0)