userver: userver/testsuite/cache_control.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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::impl {
25class ComponentBase;
26} // namespace components::impl
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 {
55 public:
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 /// @brief Reset caches with the specified @a names.
66 ///
67 /// @a update_type is used by caches derived from
68 /// @a component::CachingComponentBase.
70 cache::UpdateType update_type,
71 std::unordered_set<std::string> reset_only_names,
72 const std::unordered_set<std::string>& force_incremental_names);
73
74 CacheControl(CacheControl&&) = delete;
75 CacheControl& operator=(CacheControl&&) = delete;
76
77 /// @cond
78 // For internal use only.
79 struct UnitTests {
80 explicit UnitTests() = default;
81 };
82
83 enum class ExecPolicy {
84 kSequential,
85 kConcurrent,
86 };
87
88 CacheControl(impl::PeriodicUpdatesMode, UnitTests);
89 CacheControl(impl::PeriodicUpdatesMode, ExecPolicy, components::State);
90 ~CacheControl();
91
92 // For internal use only.
93 bool IsPeriodicUpdateEnabled(const cache::Config& cache_config,
94 const std::string& cache_name) const;
95
96 // For internal use only.
97 CacheResetRegistration RegisterPeriodicCache(cache::CacheUpdateTrait& cache);
98
99 // For internal use only. Use testsuite::RegisterCache instead
100 template <typename Component>
101 CacheResetRegistration RegisterCache(Component* self, std::string_view name,
102 void (Component::*reset_method)());
103 /// @endcond
104 private:
105 friend class CacheResetRegistration;
106
107 struct CacheInfo final {
108 std::string name;
109 std::function<void(cache::UpdateType)> reset;
110 bool needs_span{true};
111 };
112
113 struct CacheInfoNode;
114 using CacheInfoIterator = CacheInfoNode*;
115 class CacheResetJob;
116
117 void DoResetCaches(
118 cache::UpdateType update_type,
119 std::unordered_set<std::string>* reset_only_names,
120 const std::unordered_set<std::string>& force_incremental_names,
121 const std::unordered_set<std::string>* exclude_names);
122
123 void DoResetCachesConcurrently(
124 cache::UpdateType update_type,
125 std::unordered_set<std::string>* reset_only_names,
126 const std::unordered_set<std::string>& force_incremental_names,
127 const std::unordered_set<std::string>* exclude_names);
128
129 CacheInfoIterator DoRegisterCache(CacheInfo&& info);
130
131 void UnregisterCache(CacheInfoIterator) noexcept;
132
133 static void DoResetSingleCache(
134 const CacheInfo& info, cache::UpdateType update_type,
135 const std::unordered_set<std::string>& force_incremental_names);
136
137 struct Impl;
138 std::unique_ptr<Impl> impl_;
139};
140
141/// @brief RAII helper for testsuite registration. Must be kept alive to keep
142/// supporting cache resetting.
143/// @warning Make sure to always place CacheResetRegistration after the rest of
144/// the component's fields.
145/// @see testsuite::CacheControl
146class [[nodiscard]] CacheResetRegistration final {
147 public:
148 CacheResetRegistration() noexcept;
149
150 CacheResetRegistration(CacheResetRegistration&&) noexcept;
151 CacheResetRegistration& operator=(CacheResetRegistration&&) noexcept;
152 ~CacheResetRegistration();
153
154 /// Unregister the cache component explicitly.
155 /// `Unregister` is called in the destructor automatically.
156 void Unregister() noexcept;
157
158 /// @cond
159 // For internal use only.
160 CacheResetRegistration(CacheControl&, CacheControl::CacheInfoIterator);
161 /// @endcond
162
163 private:
164 CacheControl* cache_control_{nullptr};
165 CacheControl::CacheInfoIterator cache_info_iterator_{};
166};
167
168/// The method for acquiring testsuite::CacheControl in the component system.
169///
170/// @see testsuite::RegisterCache
171CacheControl& FindCacheControl(const components::ComponentContext& context);
172
173/// @brief The method for registering a cache from component constructor. The
174/// returned handle must be kept alive to keep supporting cache resetting.
175///
176/// @warning The function should be called in the component's constructor
177/// *after* all FindComponent calls. This ensures that reset will first be
178/// called for dependencies, then for dependent components.
179template <typename Component>
180CacheResetRegistration RegisterCache(
181 const components::ComponentConfig& config,
182 const components::ComponentContext& context, Component* self,
183 void (Component::*reset_method)()) {
184 auto& cc = testsuite::FindCacheControl(context);
185 return cc.RegisterCache(self, components::GetCurrentComponentName(config),
186 reset_method);
187}
188
189template <typename Component>
190CacheResetRegistration CacheControl::RegisterCache(
191 Component* self, std::string_view name, void (Component::*reset_method)()) {
192 static_assert(std::is_base_of_v<components::impl::ComponentBase, Component>,
193 "CacheControl can only be used with components");
194 UASSERT(self);
195 UASSERT(reset_method);
196
197 CacheInfo info;
198 info.name = std::string{name};
199 info.reset = [self, reset_method]([[maybe_unused]] cache::UpdateType) {
200 (self->*reset_method)();
201 };
202 info.needs_span = true;
203
204 auto iter = DoRegisterCache(std::move(info));
205 return CacheResetRegistration(*this, std::move(iter));
206}
207
208} // namespace testsuite
209
210USERVER_NAMESPACE_END