cachelib/navy/admission_policy/DynamicRandomAP.h (84 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed 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. */ #pragma once #include <folly/SharedMutex.h> #include <chrono> #include <random> #include <stdexcept> #include "cachelib/common/PercentileStats.h" #include "cachelib/navy/admission_policy/AdmissionPolicy.h" #include "gtest/gtest_prod.h" namespace facebook { namespace cachelib { namespace navy { /** * Rejects randomly and probability of rejection adjusts real-time to achieve * a target rate. * * Probability = baseProbability * probabilityFactor * * baseProbability is essentially a 1/x curve where x is the size of the item. * This means that larger items are penalized. The reasoning is that allowing * more small items increases hit ratio. Hits are measured per item, not by * size, so more items will result in more hits. * * probabilityFactor = curTargetRate_ / curRate where curTargetRate_ is the * rate needed to achieve the target culmative bytes written 24 h in the future * based on the current culmative bytes written. curRate is calculated over * updateInterval. This increases acceptance probability when we are under the * targetRate and decreases the probability when we are over. */ class DynamicRandomAP final : public AdmissionPolicy { public: using FnBytesWritten = std::function<uint64_t()>; struct Config { // Target write rate in byte/s uint64_t targetRate{0}; // Interval to update probability factor std::chrono::seconds updateInterval{60}; // Initial value for probability factor. Must be > 0. double probabilitySeed{1}; // Set base probability = 1 for this size. Must be > 0. uint32_t baseSize{4096}; // Exponent to make the probability curve shallower so larger items are not // penalized as heavily. Must be between 0 and 1. double probabilitySizeDecay{0.3}; // Probability factor change window, a (0, 1) fraction: // [1 - @changeWindow, 1 + @changeWindow] double changeWindow{0.25}; // Function that returns number of bytes written to device FnBytesWritten fnBytesWritten; // Random number generator seed uint32_t seed{1}; // when enabled(non-zero) uses the hash of the key and deterministically // admits/rejects by the hash size_t deterministicKeyHashSuffixLength{0}; // The max write rate target. 0 means disabled. // If the target write rate caluclated by write budget exceeds this, use // this rate as the target to adjust. By default this is 160MB which is // higher than the usual 120MB endurance limit. This gives some leeway for // Navy to use up its spare write budget when it wrote less than endurance // limit during trough in the traffic. uint64_t maxRate{160 * 1024 * 1024}; // Lower bound and upper bound of the probabitliy factor double probFactorLowerBound{kLowerBound_}; double probFactorUpperBound{kUpperBound_}; // Throws if invalid config Config& validate(); }; // Contructor can throw std::exception if config is invalid. // // @param config config that was validated with Config::validate // // @throw std::invalid_argument on bad config. explicit DynamicRandomAP(Config&& config); DynamicRandomAP(const DynamicRandomAP&) = delete; DynamicRandomAP& operator=(const DynamicRandomAP&) = delete; ~DynamicRandomAP() override = default; // Whether to accept the given hashed key. // The value is used to get size based probability factor. bool accept(HashedKey hk, BufferView value) override; // Reset the throttling parameters update cycle. // Not thread safe. void reset() override; // Get stats counters to export. void getCounters(const CounterVisitor& visitor) const override; void setMaxWriteRate(uint64_t maxRate) { maxRate_ = maxRate; update(); } private: struct ValidConfigTag {}; struct ThrottleParams { double probabilityFactor{1}; // The rate we can write at, given how much we've written since the host // started, from a quota standpoint. uint64_t curTargetRate{0}; // The rate that we observe since the last parameter update. uint64_t observedCurRate_{0}; uint64_t bytesWrittenLastUpdate{0}; std::chrono::seconds updateTime{0}; }; DynamicRandomAP(Config&& config, ValidConfigTag); double getBaseProbability(uint64_t size) const; void updateThrottleParams(std::chrono::seconds curTime); ThrottleParams getThrottleParams() const; double clampFactorChange(double change) const; double clampFactor(double factor) const; double genF(const HashedKey& hk) const; // Perform update to the throttling parameters. // Not thread safe. void update(); // The rate we are configered to write on average over a day. const uint64_t targetRate_{}; // The rate we are confiigured to write at most. std::atomic<uint64_t> maxRate_{}; const std::chrono::seconds updateInterval_{}; const uint32_t baseProbabilityMultiplier_{}; const double probabilitySeed_{}; const double baseProbabilityExponent_{}; const double maxChange_{}; const double minChange_{}; const FnBytesWritten fnBytesWritten_; const double lowerBound_{}; const double upperBound_{}; mutable std::minstd_rand rg_; const size_t deterministicKeyHashSuffixLength_{0}; std::chrono::seconds startupTime_{0}; mutable folly::SharedMutex mutex_; ThrottleParams params_; // baseProbability distribution on the items that are tested on accept. mutable util::PercentileStats baseProbStats_; // probabilityFactor would always be in [lowerBound_, upperBound_] static constexpr double kLowerBound_{0.001}; static constexpr double kUpperBound_{10.0}; FRIEND_TEST(DynamicRandomAPTest, AboveTarget); FRIEND_TEST(DynamicRandomAPTest, BelowTarget); FRIEND_TEST(DynamicRandomAPTest, StayInRange); FRIEND_TEST(DynamicRandomAPTest, RespectMaxWriteRate); }; } // namespace navy } // namespace cachelib } // namespace facebook