Skip to content

Commit 3a19607

Browse files
Ranjan Banerjeeanand1976
authored andcommitted
Api to get SST file with key ranges for a particular level and key range (startKey, EndKey)rocksdb [Internal version] (facebook#14009)
Summary: There are instances where an application might be interested in knowing the distribution in SST files for a key range in a particular level. This implementation creates an overloaded GetColumnFamilyMetaData api where (startKey, EndKey) can be passed along with level information to filter the necessary sst files along with the keyranges for each sst file Pull Request resolved: facebook#14009 Reviewed By: anand1976 Differential Revision: D83389707 fbshipit-source-id: 6df1dc1f9233efe9000b03cc1831b3c618cbcef3
1 parent 0a7ce82 commit 3a19607

8 files changed

Lines changed: 373 additions & 0 deletions

File tree

db/db_impl/db_impl.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5047,6 +5047,19 @@ void DBImpl::GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
50475047
}
50485048
}
50495049

5050+
void DBImpl::GetColumnFamilyMetaData(
5051+
ColumnFamilyHandle* column_family,
5052+
const GetColumnFamilyMetaDataOptions& options,
5053+
ColumnFamilyMetaData* metadata) {
5054+
assert(column_family);
5055+
auto* cfd =
5056+
static_cast_with_check<ColumnFamilyHandleImpl>(column_family)->cfd();
5057+
{
5058+
InstrumentedMutexLock l(&mutex_);
5059+
cfd->current()->GetColumnFamilyMetaData(options, metadata);
5060+
}
5061+
}
5062+
50505063
void DBImpl::GetAllColumnFamilyMetaData(
50515064
std::vector<ColumnFamilyMetaData>* metadata) {
50525065
InstrumentedMutexLock l(&mutex_);

db/db_impl/db_impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,11 @@ class DBImpl : public DB {
573573
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
574574
ColumnFamilyMetaData* metadata) override;
575575

576+
// Get column family metadata with filtering based on key range and level
577+
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
578+
const GetColumnFamilyMetaDataOptions& options,
579+
ColumnFamilyMetaData* metadata) override;
580+
576581
void GetAllColumnFamilyMetaData(
577582
std::vector<ColumnFamilyMetaData>* metadata) override;
578583

db/db_test.cc

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,246 @@ TEST_F(DBTest, MetaDataTest) {
14921492
CheckLiveFilesMeta(live_file_meta, files_by_level);
14931493
}
14941494

1495+
TEST_F(DBTest, GetColumnFamilyMetaDataWithKeyRangeAndLevel) {
1496+
Options options = CurrentOptions();
1497+
options.create_if_missing = true;
1498+
options.disable_auto_compactions = true;
1499+
1500+
int64_t temp_time = 0;
1501+
ASSERT_OK(options.env->GetCurrentTime(&temp_time));
1502+
1503+
DestroyAndReopen(options);
1504+
1505+
Random rnd(301);
1506+
int key_index = 0;
1507+
for (int i = 0; i < 100; ++i) {
1508+
// Add a single blob reference to each file
1509+
std::string blob_index;
1510+
BlobIndex::EncodeBlob(&blob_index, /* blob_file_number */ i + 1000,
1511+
/* offset */ 1234, /* size */ 5678, kNoCompression);
1512+
1513+
WriteBatch batch;
1514+
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, Key(key_index),
1515+
blob_index));
1516+
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
1517+
1518+
++key_index;
1519+
1520+
// Fill up the rest of the file with random values.
1521+
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
1522+
1523+
ASSERT_OK(Flush());
1524+
}
1525+
1526+
std::vector<std::vector<FileMetaData>> files_by_level;
1527+
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files_by_level);
1528+
1529+
ASSERT_OK(options.env->GetCurrentTime(&temp_time));
1530+
1531+
ColumnFamilyMetaData cf_meta;
1532+
// Keys in the SST files are distributed
1533+
// (key000000, key000100) ->File 1
1534+
// (key000101, key000201) -> File 2
1535+
// (key000202, key000302) -> File 3
1536+
// (key009999, key010099) -> File 100
1537+
1538+
// With keySlice (key000050, key000150) => should only pick 2 files(instead of
1539+
// default 100 that is in the level)
1540+
auto startKey = Slice("key000050");
1541+
auto endKey = Slice("key000150");
1542+
GetColumnFamilyMetaDataOptions cf_options(startKey, endKey, 0);
1543+
db_->GetColumnFamilyMetaData(cf_options, &cf_meta);
1544+
ASSERT_EQ(cf_meta.levels.size(), 1);
1545+
const auto& level_meta_from_cf = cf_meta.levels[0];
1546+
ASSERT_EQ(level_meta_from_cf.files.size(), 2);
1547+
ASSERT_LT(level_meta_from_cf.files[1].smallestkey,
1548+
std::string(startKey.data()));
1549+
ASSERT_GT(level_meta_from_cf.files[0].largestkey, std::string(endKey.data()));
1550+
1551+
GetColumnFamilyMetaDataOptions cf_option_default;
1552+
db_->GetColumnFamilyMetaData(cf_option_default, &cf_meta);
1553+
ASSERT_EQ(cf_meta.levels.size(), 1);
1554+
ASSERT_EQ(cf_meta.levels[0].files.size(), 100);
1555+
1556+
// Test with start key valid and end key unbounded
1557+
// This should get all files from key000150 onwards (99 files)
1558+
auto startKeyUnbounded = Slice("key000150");
1559+
GetColumnFamilyMetaDataOptions cf_options_unbounded_end(startKeyUnbounded,
1560+
OptSlice(), 0);
1561+
db_->GetColumnFamilyMetaData(cf_options_unbounded_end, &cf_meta);
1562+
ASSERT_EQ(cf_meta.levels.size(), 1);
1563+
ASSERT_EQ(cf_meta.levels[0].files.size(), 99);
1564+
1565+
// Test with end key valid and start key unbounded
1566+
// This should get all files from beginning to key000250 ( 3 files)
1567+
auto endKeyUnbounded = Slice("key000250");
1568+
GetColumnFamilyMetaDataOptions cf_options_unbounded_start(OptSlice(),
1569+
endKeyUnbounded, 0);
1570+
db_->GetColumnFamilyMetaData(cf_options_unbounded_start, &cf_meta);
1571+
ASSERT_EQ(cf_meta.levels.size(), 1);
1572+
ASSERT_EQ(cf_meta.levels[0].files.size(), 3);
1573+
}
1574+
1575+
TEST_F(DBTest, GetColumnFamilyMetaDataBottommostLevel) {
1576+
Options options = CurrentOptions();
1577+
options.create_if_missing = true;
1578+
options.disable_auto_compactions = true;
1579+
options.num_levels = 7;
1580+
1581+
DestroyAndReopen(options);
1582+
1583+
Random rnd(301);
1584+
int key_index = 0;
1585+
1586+
for (int i = 0; i < 100; ++i) {
1587+
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
1588+
ASSERT_OK(Flush());
1589+
}
1590+
1591+
CompactRangeOptions compact_options;
1592+
compact_options.bottommost_level_compaction =
1593+
BottommostLevelCompaction::kForce;
1594+
compact_options.change_level = true;
1595+
compact_options.target_level = 6;
1596+
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
1597+
1598+
// Nothing on Level 0 after compaction
1599+
ColumnFamilyMetaData cf_meta;
1600+
GetColumnFamilyMetaDataOptions cf_options_0(OptSlice(), OptSlice(), 0);
1601+
db_->GetColumnFamilyMetaData(cf_options_0, &cf_meta);
1602+
1603+
ASSERT_EQ(cf_meta.levels.size(), 0);
1604+
ASSERT_EQ(cf_meta.file_count, 0);
1605+
1606+
// Data should be in Level 6
1607+
GetColumnFamilyMetaDataOptions cf_options(OptSlice(), OptSlice(), 6);
1608+
db_->GetColumnFamilyMetaData(cf_options, &cf_meta);
1609+
1610+
ASSERT_EQ(cf_meta.levels.size(), 1);
1611+
ASSERT_EQ(cf_meta.levels[0].level, 6);
1612+
ASSERT_GT(cf_meta.levels[0].files.size(), 0);
1613+
size_t all_files = cf_meta.levels[0].files.size();
1614+
1615+
// Keys in the SST files are distributed across level 6
1616+
// Test with key range - should only return files within the range
1617+
auto startKey = Slice("key000050");
1618+
auto endKey = Slice("key000150");
1619+
GetColumnFamilyMetaDataOptions cf_options_range(startKey, endKey, 6);
1620+
db_->GetColumnFamilyMetaData(cf_options_range, &cf_meta);
1621+
1622+
ASSERT_EQ(cf_meta.levels.size(), 1);
1623+
ASSERT_EQ(cf_meta.levels[0].level, 6);
1624+
ASSERT_GT(cf_meta.levels[0].files.size(), 0);
1625+
size_t files_in_range = cf_meta.levels[0].files.size();
1626+
1627+
// Files in range should be less than or equal to all files
1628+
ASSERT_LE(files_in_range, all_files);
1629+
}
1630+
1631+
TEST_F(DBTest, GetColumnFamilyMetaDataMultipleLevels) {
1632+
Options options = CurrentOptions();
1633+
options.create_if_missing = true;
1634+
options.disable_auto_compactions = true;
1635+
options.num_levels = 7;
1636+
1637+
DestroyAndReopen(options);
1638+
1639+
Random rnd(301);
1640+
int key_index = 0;
1641+
1642+
for (int i = 0; i < 50; ++i) {
1643+
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
1644+
ASSERT_OK(Flush());
1645+
}
1646+
1647+
CompactRangeOptions compact_options;
1648+
compact_options.bottommost_level_compaction =
1649+
BottommostLevelCompaction::kForce;
1650+
compact_options.change_level = true;
1651+
compact_options.target_level = 6;
1652+
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
1653+
1654+
for (int i = 0; i < 30; ++i) {
1655+
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
1656+
ASSERT_OK(Flush());
1657+
}
1658+
1659+
// First verify both levels have files without key range filter
1660+
ColumnFamilyMetaData cf_meta_all_no_range;
1661+
GetColumnFamilyMetaDataOptions cf_options_all_no_range;
1662+
db_->GetColumnFamilyMetaData(cf_options_all_no_range, &cf_meta_all_no_range);
1663+
1664+
bool has_level_0 = false;
1665+
bool has_level_6 = false;
1666+
for (const auto& level : cf_meta_all_no_range.levels) {
1667+
if (level.level == 0 && level.files.size() > 0) {
1668+
has_level_0 = true;
1669+
}
1670+
if (level.level == 6 && level.files.size() > 0) {
1671+
has_level_6 = true;
1672+
}
1673+
}
1674+
1675+
ASSERT_TRUE(has_level_0);
1676+
ASSERT_TRUE(has_level_6);
1677+
1678+
// Test querying bottommost level only with key range
1679+
// Use a range that should be in the first set of files (now in level 6)
1680+
auto startKey = Slice("key000050");
1681+
auto endKey = Slice("key000150");
1682+
ColumnFamilyMetaData cf_meta_bottommost;
1683+
GetColumnFamilyMetaDataOptions cf_options_bottommost(startKey, endKey, 6);
1684+
db_->GetColumnFamilyMetaData(cf_options_bottommost, &cf_meta_bottommost);
1685+
1686+
ASSERT_EQ(cf_meta_bottommost.levels.size(), 1);
1687+
ASSERT_EQ(cf_meta_bottommost.levels[0].level, 6);
1688+
ASSERT_GT(cf_meta_bottommost.levels[0].files.size(), 0);
1689+
size_t level_6_files_in_range = cf_meta_bottommost.levels[0].files.size();
1690+
1691+
// Test querying all levels with same key range
1692+
ColumnFamilyMetaData cf_meta_all;
1693+
GetColumnFamilyMetaDataOptions cf_options_all(startKey, endKey);
1694+
db_->GetColumnFamilyMetaData(cf_options_all, &cf_meta_all);
1695+
1696+
size_t level_6_files_in_range_from_all = 0;
1697+
for (const auto& level : cf_meta_all.levels) {
1698+
if (level.level == 6) {
1699+
level_6_files_in_range_from_all = level.files.size();
1700+
}
1701+
}
1702+
1703+
ASSERT_GT(level_6_files_in_range_from_all, 0);
1704+
ASSERT_EQ(level_6_files_in_range, level_6_files_in_range_from_all);
1705+
}
1706+
1707+
TEST_F(DBTest, GetColumnFamilyMetaDataEmptyDB) {
1708+
Options options = CurrentOptions();
1709+
options.create_if_missing = true;
1710+
options.num_levels = 7;
1711+
1712+
DestroyAndReopen(options);
1713+
1714+
// Test on empty database
1715+
ColumnFamilyMetaData cf_meta_empty_db;
1716+
GetColumnFamilyMetaDataOptions cf_options_empty_db;
1717+
db_->GetColumnFamilyMetaData(cf_options_empty_db, &cf_meta_empty_db);
1718+
1719+
ASSERT_EQ(cf_meta_empty_db.levels.size(), 0);
1720+
ASSERT_EQ(cf_meta_empty_db.file_count, 0);
1721+
ASSERT_EQ(cf_meta_empty_db.size, 0);
1722+
1723+
// Test on empty database with key range
1724+
auto startKey = Slice("key000050");
1725+
auto endKey = Slice("key000150");
1726+
ColumnFamilyMetaData cf_meta_empty_range;
1727+
GetColumnFamilyMetaDataOptions cf_options_empty_range(startKey, endKey);
1728+
db_->GetColumnFamilyMetaData(cf_options_empty_range, &cf_meta_empty_range);
1729+
1730+
ASSERT_EQ(cf_meta_empty_range.levels.size(), 0);
1731+
ASSERT_EQ(cf_meta_empty_range.file_count, 0);
1732+
ASSERT_EQ(cf_meta_empty_range.size, 0);
1733+
}
1734+
14951735
TEST_F(DBTest, AllMetaDataTest) {
14961736
Options options = CurrentOptions();
14971737
options.create_if_missing = true;
@@ -3535,6 +3775,11 @@ class ModelDB : public DB {
35353775
void GetColumnFamilyMetaData(ColumnFamilyHandle* /*column_family*/,
35363776
ColumnFamilyMetaData* /*metadata*/) override {}
35373777

3778+
void GetColumnFamilyMetaData(
3779+
ColumnFamilyHandle* /*column_family*/,
3780+
const GetColumnFamilyMetaDataOptions& /*options*/,
3781+
ColumnFamilyMetaData* /*metadata*/) override {}
3782+
35383783
Status GetDbIdentity(std::string& /*identity*/) const override {
35393784
return Status::OK();
35403785
}

db/version_set.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,79 @@ void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
20242024
}
20252025
}
20262026

2027+
void Version::GetColumnFamilyMetaData(
2028+
const GetColumnFamilyMetaDataOptions& options,
2029+
ColumnFamilyMetaData* cf_meta) {
2030+
assert(cf_meta);
2031+
assert(cfd_);
2032+
2033+
cf_meta->name = cfd_->GetName();
2034+
cf_meta->size = 0;
2035+
cf_meta->file_count = 0;
2036+
cf_meta->levels.clear();
2037+
cf_meta->blob_file_size = 0;
2038+
cf_meta->blob_file_count = 0;
2039+
cf_meta->blob_files.clear();
2040+
2041+
const auto& ioptions = cfd_->ioptions();
2042+
auto* vstorage = storage_info();
2043+
2044+
int first_level = (options.level >= 0) ? options.level : 0;
2045+
int last_level =
2046+
(options.level >= 0) ? options.level + 1 : cfd_->NumberLevels();
2047+
2048+
InternalKey ikey_start, ikey_end;
2049+
const InternalKey* begin = nullptr;
2050+
const InternalKey* end = nullptr;
2051+
2052+
if (options.range.start.has_value()) {
2053+
ikey_start = InternalKey(options.range.start.value(), kMaxSequenceNumber,
2054+
kValueTypeForSeek);
2055+
begin = &ikey_start;
2056+
}
2057+
2058+
if (options.range.limit.has_value()) {
2059+
ikey_end = InternalKey(options.range.limit.value(), kMaxSequenceNumber,
2060+
kValueTypeForSeek);
2061+
end = &ikey_end;
2062+
}
2063+
2064+
for (int l = first_level; l < last_level; ++l) {
2065+
uint64_t level_size = 0;
2066+
std::vector<SstFileMetaData> files;
2067+
std::vector<FileMetaData*> overlapping_files;
2068+
vstorage->GetOverlappingInputs(l, begin, end, &overlapping_files);
2069+
2070+
for (const auto& file : overlapping_files) {
2071+
uint32_t path_id = file->fd.GetPathId();
2072+
const auto& file_path = (path_id < ioptions.cf_paths.size())
2073+
? ioptions.cf_paths[path_id].path
2074+
: ioptions.cf_paths.back().path;
2075+
const uint64_t file_number = file->fd.GetNumber();
2076+
files.emplace_back(
2077+
MakeTableFileName("", file_number), file_number, file_path,
2078+
file->fd.GetFileSize(), file->fd.smallest_seqno,
2079+
file->fd.largest_seqno, file->smallest.user_key().ToString(),
2080+
file->largest.user_key().ToString(),
2081+
file->stats.num_reads_sampled.load(std::memory_order_relaxed),
2082+
file->being_compacted, file->temperature,
2083+
file->oldest_blob_file_number, file->TryGetOldestAncesterTime(),
2084+
file->TryGetFileCreationTime(), file->epoch_number,
2085+
file->file_checksum, file->file_checksum_func_name);
2086+
files.back().num_entries = file->num_entries;
2087+
files.back().num_deletions = file->num_deletions;
2088+
files.back().smallest = file->smallest.Encode().ToString();
2089+
files.back().largest = file->largest.Encode().ToString();
2090+
level_size += file->fd.GetFileSize();
2091+
cf_meta->file_count++;
2092+
}
2093+
if (!files.empty()) {
2094+
cf_meta->levels.emplace_back(l, level_size, std::move(files));
2095+
cf_meta->size += level_size;
2096+
}
2097+
}
2098+
}
2099+
20272100
uint64_t Version::GetSstFilesSize() {
20282101
uint64_t sst_files_size = 0;
20292102
for (int level = 0; level < storage_info_.num_levels_; level++) {

db/version_set.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,10 @@ class Version {
10441044

10451045
void GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta);
10461046

1047+
// Get column family metadata with optional filtering by key range and level.
1048+
void GetColumnFamilyMetaData(const GetColumnFamilyMetaDataOptions& options,
1049+
ColumnFamilyMetaData* cf_meta);
1050+
10471051
void GetSstFilesBoundaryKeys(Slice* smallest_user_key,
10481052
Slice* largest_user_key);
10491053

0 commit comments

Comments
 (0)