// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). // #pragma once #include #include "db/version_edit.h" namespace ROCKSDB_NAMESPACE { // We boost files that are closer to TTL limit. This boosting could be // through FileMetaData.compensated_file_size but this compensated size // is widely used as something similar to file size so dramatically boost // the value might cause unintended consequences. // // This boosting algorithm can go very fancy, but here we use a simple // formula which can satisify: // (1) Different levels are triggered slightly differently to avoid // too many cascading cases // (2) Files in the same level get boosting more when TTL gets closer. // // Don't do any boosting before TTL has past by half. This is to make // sure lower write amp for most of the case. And all levels should be // fully boosted when total TTL compaction threshold triggers. // Differientiate boosting ranges of each level by 1/2. This will make // range for each level exponentially increasing. We could do it by // having them to be equal, or go even fancier. We can adjust it after // we observe the behavior in production. // The threshold starting boosting: // +------------------------------------------------------------------ + // ^ ^ ^ ^ ^ ^ // Age 0 ... | | second last level thresold // | | // | third last level // | // forth last level // // We arbitrarily set with 0 when a file is aged boost_age_start and // grow linearly. The ratio is arbitrarily set so that when the next level // starts to boost, the previous level's boosting amount is 16. class FileTtlBooster { public: FileTtlBooster(uint64_t current_time, uint64_t ttl, int num_non_empty_levels, int level) : current_time_(current_time) { if (ttl == 0 || level == 0 || level >= num_non_empty_levels - 1) { enabled_ = false; boost_age_start_ = 0; boost_step_ = 1; } else { enabled_ = true; uint64_t all_boost_start_age = ttl / 2; uint64_t all_boost_age_range = (ttl / 32) * 31 - all_boost_start_age; // TODO(cbi): more reasonable algorithm that gives different values // when num_non_empty_levels - level - 1 > 63. uint64_t boost_age_range = all_boost_age_range >> std::min(63, num_non_empty_levels - level - 1); boost_age_start_ = all_boost_start_age + boost_age_range; const uint64_t kBoostRatio = 16; // prevent 0 value to avoid divide 0 error. boost_step_ = std::max(boost_age_range / kBoostRatio, uint64_t{1}); } } uint64_t GetBoostScore(FileMetaData* f) { if (!enabled_) { return 1; } uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime(); if (oldest_ancester_time >= current_time_) { return 1; } uint64_t age = current_time_ - oldest_ancester_time; if (age > boost_age_start_) { // Use integer just for convenience. // We could make all file_to_order double if we want. // Technically this can overflow if users override timing and // give a very high current time. Ignore the case for simplicity. // Boosting is addition to current value, so +1. This will effectively // make boosting to kick in after the first boost_step_ is reached. return (age - boost_age_start_) / boost_step_ + 1; } return 1; } private: bool enabled_; uint64_t current_time_; uint64_t boost_age_start_; uint64_t boost_step_; }; } // namespace ROCKSDB_NAMESPACE