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::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