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