userver: userver/testsuite/cache_control.hpp Source File
Loading...
Searching...
No Matches
cache_control.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/testsuite/cache_control.hpp
4/// @brief @copybrief testsuite::CacheControl
5
6#include <functional>
7#include <memory>
8#include <string>
9#include <type_traits>
10#include <unordered_set>
11
12#include <userver/cache/update_type.hpp>
13#include <userver/components/component_fwd.hpp>
14#include <userver/utils/assert.hpp>
15#include <userver/utils/move_only_function.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace cache {
21struct Config;
22} // namespace cache
23
24namespace components {
26class State;
27} // namespace components
28
29namespace testsuite {
30
31namespace impl {
32enum class PeriodicUpdatesMode { kDefault, kEnabled, kDisabled };
33
34using CacheReverseDependencies = std::unordered_set<std::string>;
35
36CacheReverseDependencies GetDefaultCacheReverseDependencies();
37
38} // namespace impl
39
40class CacheResetRegistration;
41
42/// @brief Testsuite interface for caches and cache-like components.
43///
44/// If a component stores transient state that may be carried between tests,
45/// or stores caches that may become stale, then it should register its resetter
46/// here. Example:
47///
48/// @snippet core/src/testsuite/cache_control_test.cpp sample
49///
50/// Testsuite will then call this hook in the beginning of each test.
51/// You can also reset a specific cache in testsuite explicitly as follows:
52///
53/// @code
54/// service_client.invalidate_caches(names=['your-cache-name'])
55/// @endcode
56///
57/// CacheControl is normally acquired through testsuite::FindCacheControl.
58///
59/// All methods are coro-safe.
60class CacheControl final {
61public:
62 /// @brief Reset all the registered caches.
63 ///
64 /// @a update_type is used by caches derived from
65 /// @a component::CachingComponentBase.
67 cache::UpdateType update_type,
68 const std::unordered_set<std::string>& force_incremental_names,
69 const std::unordered_set<std::string>& exclude_names
70 );
71
72 /// @brief Reset caches with the specified @a names.
73 ///
74 /// @a update_type is used by caches derived from
75 /// @a component::CachingComponentBase.
77 cache::UpdateType update_type,
78 std::unordered_set<std::string> reset_only_names,
79 const std::unordered_set<std::string>& force_incremental_names
80 );
81
82 CacheControl(CacheControl&&) = delete;
83 CacheControl& operator=(CacheControl&&) = delete;
84
85 /// @cond
86 // For internal use only.
87 struct UnitTests {
88 explicit UnitTests() = default;
89 };
90
91 enum class ExecPolicy {
92 kSequential,
93 kConcurrent,
94 };
95
96 CacheControl(impl::PeriodicUpdatesMode, UnitTests);
97 CacheControl(
98 impl::PeriodicUpdatesMode,
99 ExecPolicy,
100 components::State,
101 impl::CacheReverseDependencies reverse_dependencies
102 );
103 ~CacheControl();
104
105 // For internal use only.
106 bool IsPeriodicUpdateEnabled(const cache::Config& cache_config, const std::string& cache_name) const;
107
108 // For internal use only.
109 CacheResetRegistration RegisterPeriodicCache(cache::CacheUpdateTrait& cache);
110
111 // For internal use only. Use testsuite::RegisterCacheScope instead
112 template <typename Component>
113 CacheResetRegistration RegisterCache(Component* self, std::string_view name, void (Component::*reset_method)());
114
115 struct CacheInfo final {
116 std::string name;
117 std::function<void(cache::UpdateType)> reset;
118 bool needs_span{true};
119 };
120 struct CacheInfoNode;
121 using CacheInfoIterator = CacheInfoNode*;
122
123 // For internal use only.
124 CacheInfoIterator DoRegisterCache(CacheInfo&& info);
125 /// @endcond
126private:
127 friend class CacheResetRegistration;
128
129 class CacheResetJob;
130
131 void DoResetCaches(
132 cache::UpdateType update_type,
133 std::unordered_set<std::string>* reset_only_names,
134 const std::unordered_set<std::string>& force_incremental_names,
135 const std::unordered_set<std::string>* exclude_names
136 );
137
138 void DoResetCachesConcurrently(
139 cache::UpdateType update_type,
140 std::unordered_set<std::string>* reset_only_names,
141 const std::unordered_set<std::string>& force_incremental_names,
142 const std::unordered_set<std::string>* exclude_names
143 );
144
145 void UnregisterCache(CacheInfoIterator) noexcept;
146
147 static void DoResetSingleCache(
148 const CacheInfo& info,
149 cache::UpdateType update_type,
150 const std::unordered_set<std::string>& force_incremental_names
151 );
152
153 struct Impl;
154 std::unique_ptr<Impl> impl_;
155};
156
157/// @brief RAII helper for testsuite registration.
158///
159/// Removes the associated resetter automatically on destruction.
160///
161/// Prefer @ref RegisterCacheScope so that the resetter is registered after
162/// the component constructor and unregistered just before the destructor.
163/// Otherwise store the registration as a member after the rest of
164/// the component's fields.
165/// @see testsuite::CacheControl
166class [[nodiscard]] CacheResetRegistration final {
167public:
168 CacheResetRegistration() noexcept;
169
170 CacheResetRegistration(CacheResetRegistration&&) noexcept;
171 CacheResetRegistration& operator=(CacheResetRegistration&&) noexcept;
172 ~CacheResetRegistration();
173
174 /// Unregister the cache component explicitly.
175 /// `Unregister` is called in the destructor automatically.
176 void Unregister() noexcept;
177
178 /// @cond
179 // For internal use only.
180 CacheResetRegistration(CacheControl&, CacheControl::CacheInfoIterator);
181 /// @endcond
182
183private:
184 CacheControl* cache_control_{nullptr};
185 CacheControl::CacheInfoIterator cache_info_iterator_{};
186};
187
188/// The method for acquiring testsuite::CacheControl in the component system.
189///
190/// @see testsuite::RegisterCacheScope
191CacheControl& FindCacheControl(const components::ComponentContext& context);
192
193namespace impl {
194
195void DoRegisterCacheScope(
196 const components::ComponentContext& context,
197 utils::move_only_function<CacheResetRegistration()> factory
198);
199
200} // namespace impl
201
202/// @brief Registers a cache resetter bound to the component lifetime.
203///
204/// The resetter is registered after the component constructor finishes
205/// and is unregistered just before the destructor runs.
206///
207/// Typical usage:
208/// @code
209/// testsuite::RegisterCacheScope(context, this, &MyCache::ResetCache);
210/// @endcode
211///
212/// @warning The function should be called in the component's constructor
213/// *after* all FindComponent calls. This ensures that reset will first be
214/// called for dependencies, then for dependent components.
215template <typename Component>
217 const components::ComponentContext& context,
218 Component* self,
219 void (Component::*reset_method)()
220) {
221 auto& cc = testsuite::FindCacheControl(context);
222 auto name = std::string{components::GetCurrentComponentName(context)};
223 impl::DoRegisterCacheScope(context, [&cc, self, name = std::move(name), reset_method] {
224 return cc.RegisterCache(self, name, reset_method);
225 });
226}
227
228/// @deprecated Use @ref RegisterCacheScope instead.
229/// The returned handle must be kept alive to keep supporting cache resetting.
230///
231/// @warning The function should be called in the component's constructor
232/// *after* all FindComponent calls. This ensures that reset will first be
233/// called for dependencies, then for dependent components.
234template <typename Component>
235CacheResetRegistration RegisterCache(
236 const components::ComponentContext& context,
237 Component* self,
238 void (Component::*reset_method)()
239) {
240 auto& cc = testsuite::FindCacheControl(context);
241 return cc.RegisterCache(self, components::GetCurrentComponentName(context), reset_method);
242}
243
244/// @cond
245template <typename Component>
246CacheResetRegistration CacheControl::RegisterCache(
247 Component* self,
248 std::string_view name,
249 void (Component::*reset_method)()
250) {
251 static_assert(
252 std::is_base_of_v<components::RawComponentBase, Component>,
253 "CacheControl can only be used with components"
254 );
255 UASSERT(self);
256 UASSERT(reset_method);
257
258 CacheInfo info;
259 info.name = std::string{name};
260 info.reset = [self, reset_method]([[maybe_unused]] cache::UpdateType) { (self->*reset_method)(); };
261 info.needs_span = true;
262
263 auto iter = DoRegisterCache(std::move(info));
264 return CacheResetRegistration(*this, std::move(iter));
265}
266/// @endcond
267
268} // namespace testsuite
269
270USERVER_NAMESPACE_END