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