Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1251const bool seek_compaction = (current_->file_to_compact_ != nullptr);1252if (size_compaction) {1253level = current_->compaction_level_;1254assert(level >= 0);1255assert(level + 1 < config::kNumLevels);1256c = new Compaction(options_, level);1258// Pick the first file that comes after compact_pointer_[level]1259for (size_t i = 0; i < current_->files_[level].size(); i++) {1260FileMetaData* f = current_->files_[level][i];1261if (compact_pointer_[level].empty() ||1262icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) {1263c->inputs_[0].push_back(f);1264break;1265}1266}1267if (c->inputs_[0].empty()) {1268// Wrap-around to the beginning of the key space1269c->inputs_[0].push_back(current_->files_[level][0]);1270}1271} else if (seek_compaction) {1272level = current_->file_to_compact_level_;1273c = new Compaction(options_, level);1274c->inputs_[0].push_back(current_->file_to_compact_);1275} else {1276return nullptr;1277}1279c->input_version_ = current_;1280c->input_version_->Ref();1282// Files in level 0 may overlap each other, so pick up all overlapping ones1283if (level == 0) {1284InternalKey smallest, largest;1285GetRange(c->inputs_[0], &smallest, &largest);1286// Note that the next call will discard the file we placed in1287// c->inputs_[0] earlier and replace it with an overlapping set1288// which will include the picked file.1289current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]);1290assert(!c->inputs_[0].empty());1291}1293SetupOtherInputs(c);1295return c;1296}1298// Finds the largest key in a vector of files. Returns true if files it not1299// empty.1300bool FindLargestKey(const InternalKeyComparator& icmp,1301const std::vector<FileMetaData*>& files,1302InternalKey* largest_key) {1303if (files.empty()) {1304return false;1305}1306*largest_key = files[0]->largest;1307for (size_t i = 1; i < files.size(); ++i) {1308FileMetaData* f = files[i];1309if (icmp.Compare(f->largest, *largest_key) > 0) {1310*largest_key = f->largest;1311}1312}1313return true;1314}1316// Finds minimum file b2=(l2, u2) in level file for which l2 > u1 and1317// user_key(l2) = user_key(u1)1318FileMetaData* FindSmallestBoundaryFile(1319const InternalKeyComparator& icmp,1320const std::vector<FileMetaData*>& level_files,1321const InternalKey& largest_key) {1322const Comparator* user_cmp = icmp.user_comparator();1323FileMetaData* smallest_boundary_file = nullptr;1324for (size_t i = 0; i < level_files.size(); ++i) {1325FileMetaData* f = level_files[i];1326if (icmp.Compare(f->smallest, largest_key) > 0 &&1327user_cmp->Compare(f->smallest.user_key(), largest_key.user_key()) ==13280) {1329if (smallest_boundary_file == nullptr ||1330icmp.Compare(f->smallest, smallest_boundary_file->smallest) < 0) {1331smallest_boundary_file = f;1332}1333}1334}1335return smallest_boundary_file;1336}1338// Extracts the largest file b1 from |compaction_files| and then searches for a1339// b2 in |level_files| for which user_key(u1) = user_key(l2). If it finds such a1340// file b2 (known as a boundary file) it adds it to |compaction_files| and then1341// searches again using this new upper bound.1342//1343// If there are two blocks, b1=(l1, u1) and b2=(l2, u2) and1344// user_key(u1) = user_key(l2), and if we compact b1 but not b2 then a1345// subsequent get operation will yield an incorrect result because it will1346// return the record from b2 in level i rather than from b1 because it searches1347// level by level for records matching the supplied user key.1348//1349// parameters:1350// in level_files: List of files to search for boundary files.1351// in/out compaction_files: List of files to extend by adding boundary files.1352void AddBoundaryInputs(const InternalKeyComparator& icmp,1353const std::vector<FileMetaData*>& level_files,1354std::vector<FileMetaData*>* compaction_files) {1355InternalKey largest_key;1357// Quick return if compaction_files is empty.1358if (!FindLargestKey(icmp, *compaction_files, &largest_key)) {1359return;1360}1362bool continue_searching = true;1363while (continue_searching) {1364FileMetaData* smallest_boundary_file =1365FindSmallestBoundaryFile(icmp, level_files, largest_key);1367// If a boundary file was found advance largest_key, otherwise we're done.1368if (smallest_boundary_file != NULL) {1369compaction_files->push_back(smallest_boundary_file);1370largest_key = smallest_boundary_file->largest;1371} else {1372continue_searching = false;1373}1374}1375}1377void VersionSet::SetupOtherInputs(Compaction* c) {1378const int level = c->level();1379InternalKey smallest, largest;1381AddBoundaryInputs(icmp_, current_->files_[level], &c->inputs_[0]);1382GetRange(c->inputs_[0], &smallest, &largest);1384current_->GetOverlappingInputs(level + 1, &smallest, &largest,1385&c->inputs_[1]);1387// Get entire range covered by compaction1388InternalKey all_start, all_limit;1389GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit);1391// See if we can grow the number of inputs in "level" without1392// changing the number of "level+1" files we pick up.1393if (!c->inputs_[1].empty()) {1394std::vector<FileMetaData*> expanded0;1395current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0);1396AddBoundaryInputs(icmp_, current_->files_[level], &expanded0);1397const int64_t inputs0_size = TotalFileSize(c->inputs_[0]);1398const int64_t inputs1_size = TotalFileSize(c->inputs_[1]);1399const int64_t expanded0_size = TotalFileSize(expanded0);1400if (expanded0.size() > c->inputs_[0].size() &&1401inputs1_size + expanded0_size <1402ExpandedCompactionByteSizeLimit(options_)) {1403InternalKey new_start, new_limit;1404GetRange(expanded0, &new_start, &new_limit);1405std::vector<FileMetaData*> expanded1;1406current_->GetOverlappingInputs(level + 1, &new_start, &new_limit,1407&expanded1);1408if (expanded1.size() == c->inputs_[1].size()) {1409Log(options_->info_log,1410"Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n",1411level, int(c->inputs_[0].size()), int(c->inputs_[1].size()),1412long(inputs0_size), long(inputs1_size), int(expanded0.size()),1413int(expanded1.size()), long(expanded0_size), long(inputs1_size));1414smallest = new_start;1415largest = new_limit;1416c->inputs_[0] = expanded0;1417c->inputs_[1] = expanded1;1418GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit);1419}1420}1421}1423// Compute the set of grandparent files that overlap this compaction1424// (parent == level+1; grandparent == level+2)1425if (level + 2 < config::kNumLevels) {1426current_->GetOverlappingInputs(level + 2, &all_start, &all_limit,1427&c->grandparents_);1428}1430// Update the place where we will do the next compaction for this level.1431// We update this immediately instead of waiting for the VersionEdit1432// to be applied so that if the compaction fails, we will try a different1433// key range next time.1434compact_pointer_[level] = largest.Encode().ToString();1435c->edit_.SetCompactPointer(level, largest);1436}1438Compaction* VersionSet::CompactRange(int level, const InternalKey* begin,1439const InternalKey* end) {1440std::vector<FileMetaData*> inputs;1441current_->GetOverlappingInputs(level, begin, end, &inputs);1442if (inputs.empty()) {1443return nullptr;1444}1446// Avoid compacting too much in one shot in case the range is large.1447// But we cannot do this for level-0 since level-0 files can overlap1448// and we must not pick one file and drop another older file if the1449// two files overlap.1450if (level > 0) {1451const uint64_t limit = MaxFileSizeForLevel(options_, level);1452uint64_t total = 0;1453for (size_t i = 0; i < inputs.size(); i++) {1454uint64_t s = inputs[i]->file_size;1455total += s;1456if (total >= limit) {1457inputs.resize(i + 1);1458break;1459}1460}1461}1463Compaction* c = new Compaction(options_, level);1464c->input_version_ = current_;1465c->input_version_->Ref();1466c->inputs_[0] = inputs;1467SetupOtherInputs(c);1468return c;1469}1471Compaction::Compaction(const Options* options, int level)1472: level_(level),1473max_output_file_size_(MaxFileSizeForLevel(options, level)),1474input_version_(nullptr),1475grandparent_index_(0),1476seen_key_(false),1477overlapped_bytes_(0) {1478for (int i = 0; i < config::kNumLevels; i++) {1479level_ptrs_[i] = 0;1480}1481}1483Compaction::~Compaction() {1484if (input_version_ != nullptr) {1485input_version_->Unref();1486}1487}1489bool Compaction::IsTrivialMove() const {1490const VersionSet* vset = input_version_->vset_;1491// Avoid a move if there is lots of overlapping grandparent data.1492// Otherwise, the move could create a parent file that will require1493// a very expensive merge later on.1494return (num_input_files(0) == 1 && num_input_files(1) == 0 &&1495TotalFileSize(grandparents_) <=1496MaxGrandParentOverlapBytes(vset->options_));1497}1499void Compaction::AddInputDeletions(VersionEdit* edit) {1500for (int which = 0; which < 2; which++) {
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.