1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 
10 #pragma once
11 
12 #include "rocksdb/env.h"
13 #include "rocksdb/statistics.h"
14 
15 namespace ROCKSDB_NAMESPACE {
16 
17 class RateLimiter {
18  public:
19   enum class OpType {
20     // Limitation: we currently only invoke Request() with OpType::kRead for
21     // compactions when DBOptions::new_table_reader_for_compaction_inputs is set
22     kRead,
23     kWrite,
24   };
25   enum class Mode {
26     kReadsOnly,
27     kWritesOnly,
28     kAllIo,
29   };
30 
31   // For API compatibility, default to rate-limiting writes only.
mode_(mode)32   explicit RateLimiter(Mode mode = Mode::kWritesOnly) : mode_(mode) {}
33 
~RateLimiter()34   virtual ~RateLimiter() {}
35 
36   // This API allows user to dynamically change rate limiter's bytes per second.
37   // REQUIRED: bytes_per_second > 0
38   virtual void SetBytesPerSecond(int64_t bytes_per_second) = 0;
39 
40   // Deprecated. New RateLimiter derived classes should override
41   // Request(const int64_t, const Env::IOPriority, Statistics*) or
42   // Request(const int64_t, const Env::IOPriority, Statistics*, OpType)
43   // instead.
44   //
45   // Request for token for bytes. If this request can not be satisfied, the call
46   // is blocked. Caller is responsible to make sure
47   // bytes <= GetSingleBurstBytes()
Request(const int64_t,const Env::IOPriority)48   virtual void Request(const int64_t /*bytes*/, const Env::IOPriority /*pri*/) {
49     assert(false);
50   }
51 
52   // Request for token for bytes and potentially update statistics. If this
53   // request can not be satisfied, the call is blocked. Caller is responsible to
54   // make sure bytes <= GetSingleBurstBytes().
Request(const int64_t bytes,const Env::IOPriority pri,Statistics *)55   virtual void Request(const int64_t bytes, const Env::IOPriority pri,
56                        Statistics* /* stats */) {
57     // For API compatibility, default implementation calls the older API in
58     // which statistics are unsupported.
59     Request(bytes, pri);
60   }
61 
62   // Requests token to read or write bytes and potentially updates statistics.
63   //
64   // If this request can not be satisfied, the call is blocked. Caller is
65   // responsible to make sure bytes <= GetSingleBurstBytes().
Request(const int64_t bytes,const Env::IOPriority pri,Statistics * stats,OpType op_type)66   virtual void Request(const int64_t bytes, const Env::IOPriority pri,
67                        Statistics* stats, OpType op_type) {
68     if (IsRateLimited(op_type)) {
69       Request(bytes, pri, stats);
70     }
71   }
72 
73   // Requests token to read or write bytes and potentially updates statistics.
74   // Takes into account GetSingleBurstBytes() and alignment (e.g., in case of
75   // direct I/O) to allocate an appropriate number of bytes, which may be less
76   // than the number of bytes requested.
77   virtual size_t RequestToken(size_t bytes, size_t alignment,
78                               Env::IOPriority io_priority, Statistics* stats,
79                               RateLimiter::OpType op_type);
80 
81   // Max bytes can be granted in a single burst
82   virtual int64_t GetSingleBurstBytes() const = 0;
83 
84   // Total bytes that go through rate limiter
85   virtual int64_t GetTotalBytesThrough(
86       const Env::IOPriority pri = Env::IO_TOTAL) const = 0;
87 
88   // Total # of requests that go through rate limiter
89   virtual int64_t GetTotalRequests(
90       const Env::IOPriority pri = Env::IO_TOTAL) const = 0;
91 
92   virtual int64_t GetBytesPerSecond() const = 0;
93 
IsRateLimited(OpType op_type)94   virtual bool IsRateLimited(OpType op_type) {
95     if ((mode_ == RateLimiter::Mode::kWritesOnly &&
96          op_type == RateLimiter::OpType::kRead) ||
97         (mode_ == RateLimiter::Mode::kReadsOnly &&
98          op_type == RateLimiter::OpType::kWrite)) {
99       return false;
100     }
101     return true;
102   }
103 
104  protected:
GetMode()105   Mode GetMode() { return mode_; }
106 
107  private:
108   const Mode mode_;
109 };
110 
111 // Create a RateLimiter object, which can be shared among RocksDB instances to
112 // control write rate of flush and compaction.
113 // @rate_bytes_per_sec: this is the only parameter you want to set most of the
114 // time. It controls the total write rate of compaction and flush in bytes per
115 // second. Currently, RocksDB does not enforce rate limit for anything other
116 // than flush and compaction, e.g. write to WAL.
117 // @refill_period_us: this controls how often tokens are refilled. For example,
118 // when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
119 // 100ms, then 1MB is refilled every 100ms internally. Larger value can lead to
120 // burstier writes while smaller value introduces more CPU overhead.
121 // The default should work for most cases.
122 // @fairness: RateLimiter accepts high-pri requests and low-pri requests.
123 // A low-pri request is usually blocked in favor of hi-pri request. Currently,
124 // RocksDB assigns low-pri to request from compaction and high-pri to request
125 // from flush. Low-pri requests can get blocked if flush requests come in
126 // continuously. This fairness parameter grants low-pri requests permission by
127 // 1/fairness chance even though high-pri requests exist to avoid starvation.
128 // You should be good by leaving it at default 10.
129 // @mode: Mode indicates which types of operations count against the limit.
130 // @auto_tuned: Enables dynamic adjustment of rate limit within the range
131 //              `[rate_bytes_per_sec / 20, rate_bytes_per_sec]`, according to
132 //              the recent demand for background I/O.
133 extern RateLimiter* NewGenericRateLimiter(
134     int64_t rate_bytes_per_sec, int64_t refill_period_us = 100 * 1000,
135     int32_t fairness = 10,
136     RateLimiter::Mode mode = RateLimiter::Mode::kWritesOnly,
137     bool auto_tuned = false);
138 
139 }  // namespace ROCKSDB_NAMESPACE
140