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