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
16USERVER_NAMESPACE_BEGIN
17
18namespace cache {
20struct Config;
21} // namespace cache
22
23namespace components {
25class State;
26} // namespace components
27
28namespace testsuite {
29
30namespace impl {
31enum class PeriodicUpdatesMode { kDefault, kEnabled, kDisabled };
32
33using CacheReverseDependencies = std::unordered_set<std::string>;
34
35CacheReverseDependencies GetDefaultCacheReverseDependencies();
36
37} // namespace impl
38
39class CacheResetRegistration;
40
41/// @brief Testsuite interface for caches and cache-like components.
42///
43/// If a component stores transient state that may be carried between tests,
44/// or stores caches that may become stale, then it should register its resetter
45/// here. Example:
46///
47/// @snippet testsuite/cache_control_test.cpp sample
48///
49/// Testsuite will then call this hook in the beginning of each test.
50/// You can also reset a specific cache in testsuite explicitly as follows:
51///
52/// @code
53/// service_client.invalidate_caches(names=['your-cache-name'])
54/// @endcode
55///
56/// CacheControl is normally acquired through testsuite::FindCacheControl.
57///
58/// All methods are coro-safe.
59class CacheControl final {
60public:
61 /// @brief Reset all the registered caches.
62 ///
63 /// @a update_type is used by caches derived from
64 /// @a component::CachingComponentBase.
66 cache::UpdateType update_type,
67 const std::unordered_set<std::string>& force_incremental_names,
68 const std::unordered_set<std::string>& exclude_names
69 );
70
71 /// @brief Reset caches with the specified @a names.
72 ///
73 /// @a update_type is used by caches derived from
74 /// @a component::CachingComponentBase.
76 cache::UpdateType update_type,
77 std::unordered_set<std::string> reset_only_names,
78 const std::unordered_set<std::string>& force_incremental_names
79 );
80
81 CacheControl(CacheControl&&) = delete;
82 CacheControl& operator=(CacheControl&&) = delete;
83
84 /// @cond
85 // For internal use only.
86 struct UnitTests {
87 explicit UnitTests() = default;
88 };
89
90 enum class ExecPolicy {
91 kSequential,
92 kConcurrent,
93 };
94
95 CacheControl(impl::PeriodicUpdatesMode, UnitTests);
96 CacheControl(
97 impl::PeriodicUpdatesMode,
98 ExecPolicy,
99 components::State,
100 impl::CacheReverseDependencies reverse_dependencies
101 );
102 ~CacheControl();
103
104 // For internal use only.
105 bool IsPeriodicUpdateEnabled(const cache::Config& cache_config, const std::string& cache_name) const;
106
107 // For internal use only.
108 CacheResetRegistration RegisterPeriodicCache(cache::CacheUpdateTrait& cache);
109
110 // For internal use only. Use testsuite::RegisterCache instead
111 template <typename Component>
112 CacheResetRegistration RegisterCache(Component* self, std::string_view name, void (Component::*reset_method)());
113
114 struct CacheInfo final {
115 std::string name;
116 std::function<void(cache::UpdateType)> reset;
117 bool needs_span{true};
118 };
119 struct CacheInfoNode;
120 using CacheInfoIterator = CacheInfoNode*;
121
122 // For internal use only.
123 CacheInfoIterator DoRegisterCache(CacheInfo&& info);
124 /// @endcond
125private:
126 friend class CacheResetRegistration;
127
128 class CacheResetJob;
129
130 void DoResetCaches(
131 cache::UpdateType update_type,
132 std::unordered_set<std::string>* reset_only_names,
133 const std::unordered_set<std::string>& force_incremental_names,
134 const std::unordered_set<std::string>* exclude_names
135 );
136
137 void DoResetCachesConcurrently(
138 cache::UpdateType update_type,
139 std::unordered_set<std::string>* reset_only_names,
140 const std::unordered_set<std::string>& force_incremental_names,
141 const std::unordered_set<std::string>* exclude_names
142 );
143
144 void UnregisterCache(CacheInfoIterator) noexcept;
145
146 static void DoResetSingleCache(
147 const CacheInfo& info,
148 cache::UpdateType update_type,
149 const std::unordered_set<std::string>& force_incremental_names
150 );
151
152 struct Impl;
153 std::unique_ptr<Impl> impl_;
154};
155
156/// @brief RAII helper for testsuite registration. Must be kept alive to keep
157/// supporting cache resetting.
158/// @warning Make sure to always place CacheResetRegistration after the rest of
159/// the component's fields.
160/// @see testsuite::CacheControl
161class [[nodiscard]] CacheResetRegistration final {
162public:
163 CacheResetRegistration() noexcept;
164
165 CacheResetRegistration(CacheResetRegistration&&) noexcept;
166 CacheResetRegistration& operator=(CacheResetRegistration&&) noexcept;
167 ~CacheResetRegistration();
168
169 /// Unregister the cache component explicitly.
170 /// `Unregister` is called in the destructor automatically.
171 void Unregister() noexcept;
172
173 /// @cond
174 // For internal use only.
175 CacheResetRegistration(CacheControl&, CacheControl::CacheInfoIterator);
176 /// @endcond
177
178private:
179 CacheControl* cache_control_{nullptr};
180 CacheControl::CacheInfoIterator cache_info_iterator_{};
181};
182
183/// The method for acquiring testsuite::CacheControl in the component system.
184///
185/// @see testsuite::RegisterCache
186CacheControl& FindCacheControl(const components::ComponentContext& context);
187
188/// @brief The method for registering a cache from component constructor. The
189/// returned handle must be kept alive to keep supporting cache resetting.
190///
191/// @warning The function should be called in the component's constructor
192/// *after* all FindComponent calls. This ensures that reset will first be
193/// called for dependencies, then for dependent components.
194template <typename Component>
195CacheResetRegistration RegisterCache(
196 const components::ComponentContext& context,
197 Component* self,
198 void (Component::*reset_method)()
199) {
200 auto& cc = testsuite::FindCacheControl(context);
201 return cc.RegisterCache(self, components::GetCurrentComponentName(context), reset_method);
202}
203
204/// @overload
205///
206/// @deprecated Use the overload without the `config` parameter.
207template <typename Component>
208[[deprecated("Remove 'config' parameter from RegisterCache call")]] CacheResetRegistration RegisterCache(
209 [[maybe_unused]] const components::ComponentConfig& config,
210 const components::ComponentContext& context,
211 Component* self,
212 void (Component::*reset_method)()
213) {
214 return testsuite::RegisterCache(context, self, reset_method);
215}
216
217/// @cond
218template <typename Component>
219CacheResetRegistration CacheControl::RegisterCache(
220 Component* self,
221 std::string_view name,
222 void (Component::*reset_method)()
223) {
224 static_assert(
225 std::is_base_of_v<components::RawComponentBase, Component>,
226 "CacheControl can only be used with components"
227 );
228 UASSERT(self);
229 UASSERT(reset_method);
230
231 CacheInfo info;
232 info.name = std::string{name};
233 info.reset = [self, reset_method]([[maybe_unused]] cache::UpdateType) { (self->*reset_method)(); };
234 info.needs_span = true;
235
236 auto iter = DoRegisterCache(std::move(info));
237 return CacheResetRegistration(*this, std::move(iter));
238}
239/// @endcond
240
241} // namespace testsuite
242
243USERVER_NAMESPACE_END