cache_reservation_manager.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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. #pragma once
  10. #include <atomic>
  11. #include <cstddef>
  12. #include <cstdint>
  13. #include <memory>
  14. #include <mutex>
  15. #include <vector>
  16. #include "cache/cache_entry_roles.h"
  17. #include "cache/cache_key.h"
  18. #include "cache/typed_cache.h"
  19. #include "rocksdb/slice.h"
  20. #include "rocksdb/status.h"
  21. #include "util/coding.h"
  22. namespace ROCKSDB_NAMESPACE {
  23. // CacheReservationManager is an interface for reserving cache space for the
  24. // memory used
  25. class CacheReservationManager {
  26. public:
  27. // CacheReservationHandle is for managing the lifetime of a cache reservation
  28. // for an incremental amount of memory used (i.e, incremental_memory_used)
  29. class CacheReservationHandle {
  30. public:
  31. virtual ~CacheReservationHandle() {}
  32. };
  33. virtual ~CacheReservationManager() {}
  34. virtual Status UpdateCacheReservation(std::size_t new_memory_used) = 0;
  35. // TODO(hx235): replace the usage of
  36. // `UpdateCacheReservation(memory_used_delta, increase)` with
  37. // `UpdateCacheReservation(new_memory_used)` so that we only have one
  38. // `UpdateCacheReservation` function
  39. virtual Status UpdateCacheReservation(std::size_t memory_used_delta,
  40. bool increase) = 0;
  41. virtual Status MakeCacheReservation(
  42. std::size_t incremental_memory_used,
  43. std::unique_ptr<CacheReservationManager::CacheReservationHandle>
  44. *handle) = 0;
  45. virtual std::size_t GetTotalReservedCacheSize() = 0;
  46. virtual std::size_t GetTotalMemoryUsed() = 0;
  47. };
  48. // CacheReservationManagerImpl implements interface CacheReservationManager
  49. // for reserving cache space for the memory used by inserting/releasing dummy
  50. // entries in the cache.
  51. //
  52. // This class is NOT thread-safe, except that GetTotalReservedCacheSize()
  53. // can be called without external synchronization.
  54. template <CacheEntryRole R>
  55. class CacheReservationManagerImpl
  56. : public CacheReservationManager,
  57. public std::enable_shared_from_this<CacheReservationManagerImpl<R>> {
  58. public:
  59. class CacheReservationHandle
  60. : public CacheReservationManager::CacheReservationHandle {
  61. public:
  62. CacheReservationHandle(
  63. std::size_t incremental_memory_used,
  64. std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr);
  65. ~CacheReservationHandle() override;
  66. private:
  67. std::size_t incremental_memory_used_;
  68. std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr_;
  69. };
  70. // Construct a CacheReservationManagerImpl
  71. // @param cache The cache where dummy entries are inserted and released for
  72. // reserving cache space
  73. // @param delayed_decrease If set true, then dummy entries won't be released
  74. // immediately when memory usage decreases.
  75. // Instead, it will be released when the memory usage
  76. // decreases to 3/4 of what we have reserved so far.
  77. // This is for saving some future dummy entry
  78. // insertion when memory usage increases are likely to
  79. // happen in the near future.
  80. //
  81. // REQUIRED: cache is not nullptr
  82. explicit CacheReservationManagerImpl(std::shared_ptr<Cache> cache,
  83. bool delayed_decrease = false);
  84. // no copy constructor, copy assignment, move constructor, move assignment
  85. CacheReservationManagerImpl(const CacheReservationManagerImpl &) = delete;
  86. CacheReservationManagerImpl &operator=(const CacheReservationManagerImpl &) =
  87. delete;
  88. CacheReservationManagerImpl(CacheReservationManagerImpl &&) = delete;
  89. CacheReservationManagerImpl &operator=(CacheReservationManagerImpl &&) =
  90. delete;
  91. ~CacheReservationManagerImpl() override;
  92. // One of the two ways of reserving/releasing cache space,
  93. // see MakeCacheReservation() for the other.
  94. //
  95. // Use ONLY one of these two ways to prevent unexpected behavior.
  96. //
  97. // Insert and release dummy entries in the cache to
  98. // match the size of total dummy entries with the least multiple of
  99. // kSizeDummyEntry greater than or equal to new_mem_used
  100. //
  101. // Insert dummy entries if new_memory_used > cache_allocated_size_;
  102. //
  103. // Release dummy entries if new_memory_used < cache_allocated_size_
  104. // (and new_memory_used < cache_allocated_size_ * 3/4
  105. // when delayed_decrease is set true);
  106. //
  107. // Keey dummy entries the same if (1) new_memory_used == cache_allocated_size_
  108. // or (2) new_memory_used is in the interval of
  109. // [cache_allocated_size_ * 3/4, cache_allocated_size) when delayed_decrease
  110. // is set true.
  111. //
  112. // @param new_memory_used The number of bytes used by new memory
  113. // The most recent new_memoy_used passed in will be returned
  114. // in GetTotalMemoryUsed() even when the call return non-ok status.
  115. //
  116. // Since the class is NOT thread-safe, external synchronization on the
  117. // order of calling UpdateCacheReservation() is needed if you want
  118. // GetTotalMemoryUsed() indeed returns the latest memory used.
  119. //
  120. // @return On inserting dummy entries, it returns Status::OK() if all dummy
  121. // entry insertions succeed.
  122. // Otherwise, it returns the first non-ok status;
  123. // On releasing dummy entries, it always returns Status::OK().
  124. // On keeping dummy entries the same, it always returns Status::OK().
  125. Status UpdateCacheReservation(std::size_t new_memory_used) override;
  126. Status UpdateCacheReservation(std::size_t /* memory_used_delta */,
  127. bool /* increase */) override {
  128. return Status::NotSupported();
  129. }
  130. // One of the two ways of reserving cache space and releasing is done through
  131. // destruction of CacheReservationHandle.
  132. // See UpdateCacheReservation() for the other way.
  133. //
  134. // Use ONLY one of these two ways to prevent unexpected behavior.
  135. //
  136. // Insert dummy entries in the cache for the incremental memory usage
  137. // to match the size of total dummy entries with the least multiple of
  138. // kSizeDummyEntry greater than or equal to the total memory used.
  139. //
  140. // A CacheReservationHandle is returned as an output parameter.
  141. // The reserved dummy entries are automatically released on the destruction of
  142. // this handle, which achieves better RAII per cache reservation.
  143. //
  144. // WARNING: Deallocate all the handles of the CacheReservationManager object
  145. // before deallocating the object to prevent unexpected behavior.
  146. //
  147. // @param incremental_memory_used The number of bytes increased in memory
  148. // usage.
  149. //
  150. // Calling GetTotalMemoryUsed() afterward will return the total memory
  151. // increased by this number, even when calling MakeCacheReservation()
  152. // returns non-ok status.
  153. //
  154. // Since the class is NOT thread-safe, external synchronization in
  155. // calling MakeCacheReservation() is needed if you want
  156. // GetTotalMemoryUsed() indeed returns the latest memory used.
  157. //
  158. // @param handle An pointer to std::unique_ptr<CacheReservationHandle> that
  159. // manages the lifetime of the cache reservation represented by the
  160. // handle.
  161. //
  162. // @return It returns Status::OK() if all dummy
  163. // entry insertions succeed.
  164. // Otherwise, it returns the first non-ok status;
  165. //
  166. // REQUIRES: handle != nullptr
  167. Status MakeCacheReservation(
  168. std::size_t incremental_memory_used,
  169. std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
  170. override;
  171. // Return the size of the cache (which is a multiple of kSizeDummyEntry)
  172. // successfully reserved by calling UpdateCacheReservation().
  173. //
  174. // When UpdateCacheReservation() returns non-ok status,
  175. // calling GetTotalReservedCacheSize() after that might return a slightly
  176. // smaller number than the actual reserved cache size due to
  177. // the returned number will always be a multiple of kSizeDummyEntry
  178. // and cache full might happen in the middle of inserting a dummy entry.
  179. std::size_t GetTotalReservedCacheSize() override;
  180. // Return the latest total memory used indicated by the most recent call of
  181. // UpdateCacheReservation(std::size_t new_memory_used);
  182. std::size_t GetTotalMemoryUsed() override;
  183. static constexpr std::size_t GetDummyEntrySize() { return kSizeDummyEntry; }
  184. // For testing only - it is to help ensure the CacheItemHelperForRole<R>
  185. // accessed from CacheReservationManagerImpl and the one accessed from the
  186. // test are from the same translation units
  187. static const Cache::CacheItemHelper *TEST_GetCacheItemHelperForRole();
  188. private:
  189. static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
  190. Slice GetNextCacheKey();
  191. Status ReleaseCacheReservation(std::size_t incremental_memory_used);
  192. Status IncreaseCacheReservation(std::size_t new_mem_used);
  193. Status DecreaseCacheReservation(std::size_t new_mem_used);
  194. using CacheInterface = PlaceholderSharedCacheInterface<R>;
  195. CacheInterface cache_;
  196. bool delayed_decrease_;
  197. std::atomic<std::size_t> cache_allocated_size_;
  198. std::size_t memory_used_;
  199. std::vector<Cache::Handle *> dummy_handles_;
  200. CacheKey cache_key_;
  201. };
  202. class ConcurrentCacheReservationManager
  203. : public CacheReservationManager,
  204. public std::enable_shared_from_this<ConcurrentCacheReservationManager> {
  205. public:
  206. class CacheReservationHandle
  207. : public CacheReservationManager::CacheReservationHandle {
  208. public:
  209. CacheReservationHandle(
  210. std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr,
  211. std::unique_ptr<CacheReservationManager::CacheReservationHandle>
  212. cache_res_handle) {
  213. assert(cache_res_mgr && cache_res_handle);
  214. cache_res_mgr_ = cache_res_mgr;
  215. cache_res_handle_ = std::move(cache_res_handle);
  216. }
  217. ~CacheReservationHandle() override {
  218. std::lock_guard<std::mutex> lock(cache_res_mgr_->cache_res_mgr_mu_);
  219. cache_res_handle_.reset();
  220. }
  221. private:
  222. std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
  223. std::unique_ptr<CacheReservationManager::CacheReservationHandle>
  224. cache_res_handle_;
  225. };
  226. explicit ConcurrentCacheReservationManager(
  227. std::shared_ptr<CacheReservationManager> cache_res_mgr) {
  228. cache_res_mgr_ = std::move(cache_res_mgr);
  229. }
  230. ConcurrentCacheReservationManager(const ConcurrentCacheReservationManager &) =
  231. delete;
  232. ConcurrentCacheReservationManager &operator=(
  233. const ConcurrentCacheReservationManager &) = delete;
  234. ConcurrentCacheReservationManager(ConcurrentCacheReservationManager &&) =
  235. delete;
  236. ConcurrentCacheReservationManager &operator=(
  237. ConcurrentCacheReservationManager &&) = delete;
  238. ~ConcurrentCacheReservationManager() override {}
  239. inline Status UpdateCacheReservation(std::size_t new_memory_used) override {
  240. std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
  241. return cache_res_mgr_->UpdateCacheReservation(new_memory_used);
  242. }
  243. inline Status UpdateCacheReservation(std::size_t memory_used_delta,
  244. bool increase) override {
  245. std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
  246. std::size_t total_mem_used = cache_res_mgr_->GetTotalMemoryUsed();
  247. Status s;
  248. if (!increase) {
  249. s = cache_res_mgr_->UpdateCacheReservation(
  250. (total_mem_used > memory_used_delta)
  251. ? (total_mem_used - memory_used_delta)
  252. : 0);
  253. } else {
  254. s = cache_res_mgr_->UpdateCacheReservation(total_mem_used +
  255. memory_used_delta);
  256. }
  257. return s;
  258. }
  259. inline Status MakeCacheReservation(
  260. std::size_t incremental_memory_used,
  261. std::unique_ptr<CacheReservationManager::CacheReservationHandle> *handle)
  262. override {
  263. std::unique_ptr<CacheReservationManager::CacheReservationHandle>
  264. wrapped_handle;
  265. Status s;
  266. {
  267. std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
  268. s = cache_res_mgr_->MakeCacheReservation(incremental_memory_used,
  269. &wrapped_handle);
  270. }
  271. (*handle).reset(
  272. new ConcurrentCacheReservationManager::CacheReservationHandle(
  273. std::enable_shared_from_this<
  274. ConcurrentCacheReservationManager>::shared_from_this(),
  275. std::move(wrapped_handle)));
  276. return s;
  277. }
  278. inline std::size_t GetTotalReservedCacheSize() override {
  279. return cache_res_mgr_->GetTotalReservedCacheSize();
  280. }
  281. inline std::size_t GetTotalMemoryUsed() override {
  282. std::lock_guard<std::mutex> lock(cache_res_mgr_mu_);
  283. return cache_res_mgr_->GetTotalMemoryUsed();
  284. }
  285. private:
  286. std::mutex cache_res_mgr_mu_;
  287. std::shared_ptr<CacheReservationManager> cache_res_mgr_;
  288. };
  289. } // namespace ROCKSDB_NAMESPACE