userver: userver/cache/lru_cache_component_base.hpp Source File
Loading...
Searching...
No Matches
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