userver: userver/cache/lru_cache_component_base.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
lru_cache_component_base.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/cache/lru_cache_component_base.hpp
4/// @brief @copybrief cache::LruCacheComponent
5
6#include <functional>
7
8#include <userver/cache/expirable_lru_cache.hpp>
9#include <userver/cache/lru_cache_config.hpp>
10#include <userver/components/loggable_component_base.hpp>
11#include <userver/concurrent/async_event_source.hpp>
12#include <userver/dump/dumper.hpp>
13#include <userver/dump/meta.hpp>
14#include <userver/dump/operations.hpp>
15#include <userver/dynamic_config/source.hpp>
16#include <userver/logging/log.hpp>
17#include <userver/testsuite/component_control.hpp>
18#include <userver/utils/statistics/entry.hpp>
19#include <userver/yaml_config/schema.hpp>
20
21USERVER_NAMESPACE_BEGIN
22
23namespace cache {
24
25namespace impl {
26
27testsuite::ComponentControl& FindComponentControl(
28 const components::ComponentContext& context);
29
30utils::statistics::Entry RegisterOnStatisticsStorage(
31 const components::ComponentContext& context, const std::string& name,
32 std::function<void(utils::statistics::Writer&)> func);
33
34dynamic_config::Source FindDynamicConfigSource(
35 const components::ComponentContext& context);
36
37bool IsDumpSupportEnabled(const components::ComponentConfig& config);
38
39yaml_config::Schema GetLruCacheComponentBaseSchema();
40
41} // namespace impl
42
43// clang-format off
44
45/// @ingroup userver_components userver_base_classes
46///
47/// @brief Base class for LRU-cache components
48///
49/// Provides facilities for creating LRU caches.
50/// You need to override LruCacheComponent::DoGetByKey to handle cache misses.
51///
52/// Caching components must be configured in service config (see options below)
53/// and may be reconfigured dynamically via components::DynamicConfig.
54///
55/// ## Dynamic config
56/// * @ref USERVER_LRU_CACHES
57///
58/// ## Static options:
59/// Name | Description | Default value
60/// ---- | ----------- | -------------
61/// size | max amount of items to store in cache | --
62/// ways | number of ways for associative cache | --
63/// lifetime | TTL for cache entries (0 is unlimited) | 0
64/// config-settings | enables dynamic reconfiguration with CacheConfigSet | true
65///
66/// ## Example usage:
67///
68/// @snippet cache/lru_cache_component_base_test.hpp Sample lru cache component
69///
70/// Do not forget to @ref userver_components "add the component to component list":
71/// @snippet cache/lru_cache_component_base_test.cpp Sample lru cache component registration
72///
73/// ## Example config:
74/// @snippet cache/lru_cache_component_base_test.cpp Sample lru cache component config
75
76// clang-format on
77template <typename Key, typename Value, typename Hash = std::hash<Key>,
78 typename Equal = std::equal_to<Key>>
79// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
81 private dump::DumpableEntity {
82 public:
83 using Cache = ExpirableLruCache<Key, Value, Hash, Equal>;
84 using CacheWrapper = LruCacheWrapper<Key, Value, Hash, Equal>;
85
86 LruCacheComponent(const components::ComponentConfig&,
87 const components::ComponentContext&);
88
89 ~LruCacheComponent() override;
90
91 CacheWrapper GetCache();
92
93 static yaml_config::Schema GetStaticConfigSchema();
94
95 protected:
96 virtual Value DoGetByKey(const Key& key) = 0;
97
98 private:
99 void DropCache();
100
101 Value GetByKey(const Key& key);
102
103 void OnConfigUpdate(const dynamic_config::Snapshot& cfg);
104
105 void UpdateConfig(const LruCacheConfig& config);
106
107 static constexpr bool kCacheIsDumpable =
108 dump::kIsDumpable<Key> && dump::kIsDumpable<Value>;
109
110 void GetAndWrite(dump::Writer& writer) const override;
111 void ReadAndSet(dump::Reader& reader) override;
112
113 protected:
114 std::shared_ptr<Cache> GetCacheRaw() { return cache_; }
115
116 private:
117 const std::string name_;
118 const LruCacheConfigStatic static_config_;
119 std::shared_ptr<dump::Dumper> dumper_;
120 const std::shared_ptr<Cache> cache_;
121 concurrent::AsyncEventSubscriberScope config_subscription_;
122 utils::statistics::Entry statistics_holder_;
123 std::optional<testsuite::ComponentInvalidatorHolder> invalidator_holder_;
124};
125
126template <typename Key, typename Value, typename Hash, typename Equal>
127LruCacheComponent<Key, Value, Hash, Equal>::LruCacheComponent(
128 const components::ComponentConfig& config,
129 const components::ComponentContext& context)
130 : LoggableComponentBase(config, context),
132 static_config_(config),
133 cache_(std::make_shared<Cache>(static_config_.ways,
134 static_config_.GetWaySize())) {
135 if (impl::IsDumpSupportEnabled(config)) {
136 dumper_ = std::make_shared<dump::Dumper>(
137 config, context, static_cast<dump::DumpableEntity&>(*this));
138 cache_->SetDumper(dumper_);
139 dumper_->ReadDump();
140 }
141
142 cache_->SetMaxLifetime(static_config_.config.lifetime);
143 cache_->SetBackgroundUpdate(static_config_.config.background_update);
144
145 if (static_config_.use_dynamic_config) {
146 LOG_INFO() << "Dynamic LRU cache config is enabled, subscribing on "
147 "dynamic-config updates, cache="
148 << name_;
149
150 config_subscription_ =
151 impl::FindDynamicConfigSource(context).UpdateAndListen(
152 this, "cache." + name_,
153 &LruCacheComponent<Key, Value, Hash, Equal>::OnConfigUpdate);
154 } else {
155 LOG_INFO() << "Dynamic LRU cache config is disabled, cache=" << name_;
156 }
157
158 statistics_holder_ = impl::RegisterOnStatisticsStorage(
159 context, name_,
160 [this](utils::statistics::Writer& writer) { writer = *cache_; });
161
162 invalidator_holder_.emplace(
163 impl::FindComponentControl(context), *this,
164 &LruCacheComponent<Key, Value, Hash, Equal>::DropCache);
165}
166
167template <typename Key, typename Value, typename Hash, typename Equal>
168LruCacheComponent<Key, Value, Hash, Equal>::~LruCacheComponent() {
169 invalidator_holder_.reset();
170 statistics_holder_.Unregister();
171 config_subscription_.Unsubscribe();
172
173 if (dumper_) {
174 dumper_->CancelWriteTaskAndWait();
175 }
176}
177
178template <typename Key, typename Value, typename Hash, typename Equal>
179typename LruCacheComponent<Key, Value, Hash, Equal>::CacheWrapper
180LruCacheComponent<Key, Value, Hash, Equal>::GetCache() {
181 return CacheWrapper(cache_, [this](const Key& key) { return GetByKey(key); });
182}
183
184template <typename Key, typename Value, typename Hash, typename Equal>
185void LruCacheComponent<Key, Value, Hash, Equal>::DropCache() {
186 cache_->Invalidate();
187}
188
189template <typename Key, typename Value, typename Hash, typename Equal>
190Value LruCacheComponent<Key, Value, Hash, Equal>::GetByKey(const Key& key) {
191 return DoGetByKey(key);
192}
193
194template <typename Key, typename Value, typename Hash, typename Equal>
195void LruCacheComponent<Key, Value, Hash, Equal>::OnConfigUpdate(
196 const dynamic_config::Snapshot& cfg) {
197 const auto config = GetLruConfig(cfg, name_);
198 if (config) {
199 LOG_DEBUG() << "Using dynamic config for LRU cache";
200 UpdateConfig(*config);
201 } else {
202 LOG_DEBUG() << "Using static config for LRU cache";
203 UpdateConfig(static_config_.config);
204 }
205}
206
207template <typename Key, typename Value, typename Hash, typename Equal>
208void LruCacheComponent<Key, Value, Hash, Equal>::UpdateConfig(
209 const LruCacheConfig& config) {
210 cache_->SetWaySize(config.GetWaySize(static_config_.ways));
211 cache_->SetMaxLifetime(config.lifetime);
212 cache_->SetBackgroundUpdate(config.background_update);
213}
214
215template <typename Key, typename Value, typename Hash, typename Equal>
216yaml_config::Schema
217LruCacheComponent<Key, Value, Hash, Equal>::GetStaticConfigSchema() {
218 return impl::GetLruCacheComponentBaseSchema();
219}
220
221template <typename Key, typename Value, typename Hash, typename Equal>
222void LruCacheComponent<Key, Value, Hash, Equal>::GetAndWrite(
223 dump::Writer& writer) const {
224 if constexpr (kCacheIsDumpable) {
225 cache_->Write(writer);
226 } else {
227 dump::ThrowDumpUnimplemented(name_);
228 }
229}
230
231template <typename Key, typename Value, typename Hash, typename Equal>
232void LruCacheComponent<Key, Value, Hash, Equal>::ReadAndSet(
233 dump::Reader& reader) {
234 if constexpr (kCacheIsDumpable) {
235 cache_->Read(reader);
236 } else {
237 dump::ThrowDumpUnimplemented(name_);
238 }
239}
240
241} // namespace cache
242
243USERVER_NAMESPACE_END