blob: 0e63cd10fe341e7076a39fb4bbe14bd88a9cd1ab [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "compaction_checker.h"
#include "logging.h"
#include "parse_util.h"
#include "storage.h"
#include "time_util.h"
void CompactionChecker::CompactPropagateAndPubSubFiles() {
rocksdb::CompactRangeOptions compact_opts;
// See https://github.com/facebook/rocksdb/issues/13671
// change_level doesn't work well with level_compaction_dynamic_level_bytes
compact_opts.change_level = !storage_->GetConfig()->rocks_db.level_compaction_dynamic_level_bytes;
for (const auto &cf :
{engine::ColumnFamilyConfigs::PubSubColumnFamily(), engine::ColumnFamilyConfigs::PropagateColumnFamily()}) {
INFO("[compaction checker] Start to compact the column family: {}", cf.Name());
auto cf_handle = storage_->GetCFHandle(cf.Id());
auto s = storage_->GetDB()->CompactRange(compact_opts, cf_handle, nullptr, nullptr);
INFO("[compaction checker] Compact the column family: {} finished, result: {}", cf.Name(), s.ToString());
}
}
void CompactionChecker::PickCompactionFilesForCf(const engine::ColumnFamilyConfig &column_family_config) {
rocksdb::TablePropertiesCollection props;
rocksdb::ColumnFamilyHandle *cf = storage_->GetCFHandle(column_family_config.Id());
auto s = storage_->GetDB()->GetPropertiesOfAllTables(cf, &props);
if (!s.ok()) {
WARN("[compaction checker] Failed to get table properties, {}", s.ToString());
return;
}
// The main goal of compaction was reclaimed the disk space and removed
// the tombstone. It seems that compaction checker was unnecessary here when
// the live files was too few, Hard code to 1 here.
if (props.size() <= 1) return;
size_t max_files_to_compact = 1;
if (props.size() / 360 > max_files_to_compact) {
max_files_to_compact = props.size() / 360;
}
int64_t now = util::GetTimeStamp();
auto force_compact_file_age = storage_->GetConfig()->force_compact_file_age;
auto force_compact_min_ratio =
static_cast<double>(storage_->GetConfig()->force_compact_file_min_deleted_percentage) / 100.0;
std::string best_filename;
double best_delete_ratio = 0;
int64_t total_keys = 0, deleted_keys = 0;
rocksdb::Slice start_key, stop_key, best_start_key, best_stop_key;
for (const auto &iter : props) {
if (max_files_to_compact == 0) return;
uint64_t file_creation_time = iter.second->file_creation_time;
if (file_creation_time == 0) {
// Fallback to the file Modification time to prevent repeatedly compacting the same file,
// file_creation_time is 0 which means the unknown condition in rocksdb
s = rocksdb::Env::Default()->GetFileModificationTime(iter.first, &file_creation_time);
if (!s.ok()) {
INFO("[compaction checker] Failed to get the file creation time: {}, err: {}", iter.first, s.ToString());
continue;
}
}
for (const auto &property_iter : iter.second->user_collected_properties) {
if (property_iter.first == "total_keys") {
auto parse_result = ParseInt<int>(property_iter.second, 10);
if (!parse_result) {
ERROR("[compaction checker] Parse total_keys error: {}", parse_result.Msg());
continue;
}
total_keys = *parse_result;
}
if (property_iter.first == "deleted_keys") {
auto parse_result = ParseInt<int>(property_iter.second, 10);
if (!parse_result) {
ERROR("[compaction checker] Parse deleted_keys error: {}", parse_result.Msg());
continue;
}
deleted_keys = *parse_result;
}
if (property_iter.first == "start_key") {
start_key = property_iter.second;
}
if (property_iter.first == "stop_key") {
stop_key = property_iter.second;
}
}
if (start_key.empty() || stop_key.empty()) continue;
double delete_ratio = static_cast<double>(deleted_keys) / static_cast<double>(total_keys);
// pick the file according to force compact policy
if (file_creation_time < static_cast<uint64_t>(now - force_compact_file_age) &&
delete_ratio >= force_compact_min_ratio) {
INFO("[compaction checker] Going to compact the key in file (force compact policy): {}", iter.first);
auto s = storage_->Compact(cf, &start_key, &stop_key);
INFO("[compaction checker] Compact the key in file (force compact policy): {} finished, result: {}", iter.first,
s.ToString());
max_files_to_compact--;
continue;
}
// don't compact the SST created in 1 hour
if (file_creation_time > static_cast<uint64_t>(now - 3600)) continue;
// pick the file which has highest delete ratio
if (total_keys != 0 && delete_ratio > best_delete_ratio) {
best_delete_ratio = delete_ratio;
best_filename = iter.first;
best_start_key = start_key;
start_key.clear();
best_stop_key = stop_key;
stop_key.clear();
}
}
if (best_delete_ratio > 0.1 && !best_start_key.empty() && !best_stop_key.empty()) {
INFO("[compaction checker] Going to compact the key in file: {}, delete ratio: {}", best_filename,
best_delete_ratio);
auto s = storage_->Compact(cf, &best_start_key, &best_stop_key);
if (!s.ok()) {
ERROR("[compaction checker] Failed to do compaction: {}", s.ToString());
}
}
}