userver: userver/dynamic_config/source.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
source.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/dynamic_config/source.hpp
4/// @brief @copybrief dynamic_config::Source
5
6#include <string_view>
7#include <tuple>
8#include <utility>
9
10#include <userver/concurrent/async_event_source.hpp>
11#include <userver/dynamic_config/snapshot.hpp>
12#include <userver/utils/assert.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace dynamic_config {
17
18/// Owns a snapshot of a config variable. You may use operator* or operator->
19/// to access the config variable.
20///
21/// `VariableSnapshotPtr` in only intended to be used locally. Don't store it
22/// as a class member or pass it between functions. Use `Snapshot` for that
23/// purpose.
24template <typename Key>
25class VariableSnapshotPtr final {
26 public:
27 VariableSnapshotPtr(VariableSnapshotPtr&&) = delete;
28 VariableSnapshotPtr& operator=(VariableSnapshotPtr&&) = delete;
29
30 const VariableOfKey<Key>& operator*() const& { return variable_; }
31 const VariableOfKey<Key>& operator*() && { ReportMisuse(); }
32
33 const VariableOfKey<Key>* operator->() const& { return &variable_; }
34 const VariableOfKey<Key>* operator->() && { ReportMisuse(); }
35
36 private:
37 [[noreturn]] static void ReportMisuse() {
38 static_assert(!sizeof(Key), "keep the pointer before using, please");
39 }
40
41 explicit VariableSnapshotPtr(Snapshot&& snapshot, Key key)
42 : snapshot_(std::move(snapshot)), variable_(snapshot_[key]) {}
43
44 // for the constructor
45 friend class Source;
46
47 Snapshot snapshot_;
48 const VariableOfKey<Key>& variable_;
49};
50
51/// @brief Helper class for subscribing to dynamic-config updates with a custom
52/// callback.
53///
54/// Stores information about the last update that occurred.
55///
56/// @param previous `dynamic_config::Snapshot` of the previous config or
57/// `std::nullopt` if this update event is the first for the subscriber.
58struct Diff final {
59 std::optional<Snapshot> previous;
60 Snapshot current;
61};
62
63// clang-format off
64
65/// @ingroup userver_clients
66///
67/// @brief A client for easy dynamic config fetching in components.
68///
69/// After construction, dynamic_config::Source
70/// can be copied around and passed to clients or child helper classes.
71///
72/// Usually retrieved from components::DynamicConfig component.
73///
74/// Typical usage:
75/// @snippet components/component_sample_test.cpp Sample user component runtime config source
76
77// clang-format on
78class Source final {
79 public:
80 using SnapshotEventSource = concurrent::AsyncEventSource<const Snapshot&>;
81 using DiffEventSource = concurrent::AsyncEventSource<const Diff&>;
82
83 /// For internal use only. Obtain using components::DynamicConfig or
84 /// dynamic_config::StorageMock instead.
85 explicit Source(impl::StorageData& storage);
86
87 // trivially copyable
88 Source(const Source&) = default;
89 Source(Source&&) = default;
90 Source& operator=(const Source&) = default;
91 Source& operator=(Source&&) = default;
92
93 Snapshot GetSnapshot() const;
94
95 template <typename Key>
96 VariableSnapshotPtr<Key> GetSnapshot(Key key) const {
97 return VariableSnapshotPtr{GetSnapshot(), key};
98 }
99
100 template <typename Key>
101 VariableOfKey<Key> GetCopy(Key key) const {
102 const auto snapshot = GetSnapshot();
103 return snapshot[key];
104 }
105
106 /// Subscribes to dynamic-config updates using a member function. Also
107 /// immediately invokes the function with the current config snapshot (this
108 /// invocation will be executed synchronously).
109 ///
110 /// @note Callbacks occur in full accordance with
111 /// `components::DynamicConfigClientUpdater` options.
112 ///
113 /// @param obj the subscriber, which is the owner of the listener method, and
114 /// is also used as the unique identifier of the subscription
115 /// @param name the name of the subscriber, for diagnostic purposes
116 /// @param func the listener method, named `OnConfigUpdate` by convention.
117 /// @returns a `concurrent::AsyncEventSubscriberScope` controlling the
118 /// subscription, which should be stored as a member in the subscriber;
119 /// `Unsubscribe` should be called explicitly
120 ///
121 /// @see based on concurrent::AsyncEventSource engine
122 template <typename Class>
123 concurrent::AsyncEventSubscriberScope UpdateAndListen(
124 Class* obj, std::string_view name,
125 void (Class::*func)(const dynamic_config::Snapshot& config)) {
126 return DoUpdateAndListen(
127 concurrent::FunctionId(obj), name,
128 [obj, func](const dynamic_config::Snapshot& config) {
129 (obj->*func)(config);
130 });
131 }
132
133 // clang-format off
134
135 /// @brief Subscribes to dynamic-config updates with information about the
136 /// current and previous states.
137 ///
138 /// Subscribes to dynamic-config updates using a member function, named
139 /// `OnConfigUpdate` by convention. Also constructs `dynamic_config::Diff`
140 /// object using `std::nullopt` and current config snapshot, then immediately
141 /// invokes the function with it (this invocation will be executed
142 /// synchronously).
143 ///
144 /// @note Callbacks occur in full accordance with
145 /// `components::DynamicConfigClientUpdater` options.
146 ///
147 /// @warning In debug mode the last notification for any subscriber will be
148 /// called with `std::nullopt` and current config snapshot.
149 ///
150 /// Example usage:
151 /// @snippet dynamic_config/config_test.cpp Custom subscription for dynamic config update
152 ///
153 /// @param obj the subscriber, which is the owner of the listener method, and
154 /// is also used as the unique identifier of the subscription
155 /// @param name the name of the subscriber, for diagnostic purposes
156 /// @param func the listener method, named `OnConfigUpdate` by convention.
157 /// @returns a `concurrent::AsyncEventSubscriberScope` controlling the
158 /// subscription, which should be stored as a member in the subscriber;
159 /// `Unsubscribe` should be called explicitly
160 ///
161 /// @see based on concurrent::AsyncEventSource engine
162 ///
163 /// @see dynamic_config::Diff
164
165 // clang-format on
166 template <typename Class>
167 concurrent::AsyncEventSubscriberScope UpdateAndListen(
168 Class* obj, std::string_view name,
169 void (Class::*func)(const dynamic_config::Diff& diff)) {
170 return DoUpdateAndListen(
171 concurrent::FunctionId(obj), name,
172 [obj, func](const dynamic_config::Diff& diff) { (obj->*func)(diff); });
173 }
174
175 /// @brief Subscribes to updates of a subset of all configs.
176 ///
177 /// Subscribes to dynamic-config updates using a member function, named
178 /// `OnConfigUpdate` by convention. The function will be invoked if at least
179 /// one of the configs has been changed since the previous invocation. So at
180 /// the first time immediately invokes the function with the current config
181 /// snapshot (this invocation will be executed synchronously).
182 ///
183 /// @note Сallbacks occur only if one of the passed config is changed. This is
184 /// true under any components::DynamicConfigClientUpdater options.
185 ///
186 /// @warning To use this function, configs must have the `operator==`.
187 ///
188 /// @param obj the subscriber, which is the owner of the listener method, and
189 /// is also used as the unique identifier of the subscription
190 /// @param name the name of the subscriber, for diagnostic purposes
191 /// @param func the listener method, named `OnConfigUpdate` by convention.
192 /// @param keys config objects, specializations of `dynamic_config::Key`.
193 /// @returns a `concurrent::AsyncEventSubscriberScope` controlling the
194 /// subscription, which should be stored as a member in the subscriber;
195 /// `Unsubscribe` should be called explicitly
196 ///
197 /// @see based on concurrent::AsyncEventSource engine
198 template <typename Class, typename... Keys>
199 concurrent::AsyncEventSubscriberScope UpdateAndListen(
200 Class* obj, std::string_view name,
201 void (Class::*func)(const dynamic_config::Snapshot& config),
202 Keys... keys) {
203 auto wrapper = [obj, func,
204 keys = std::make_tuple(keys...)](const Diff& diff) {
205 const auto args = std::tuple_cat(std::tie(diff), keys);
206 if (!std::apply(HasChanged<Keys...>, args)) return;
207 (obj->*func)(diff.current);
208 };
209 return DoUpdateAndListen(concurrent::FunctionId(obj), name,
210 std::move(wrapper));
211 }
212
213 SnapshotEventSource& GetEventChannel();
214
215 private:
216 template <typename... Keys>
217 static bool HasChanged(const Diff& diff, Keys... keys) {
218 if (!diff.previous) return true;
219
220 const auto& previous = *diff.previous;
221 const auto& current = diff.current;
222
223 UASSERT(!current.GetData().IsEmpty());
224 UASSERT(!previous.GetData().IsEmpty());
225
226 const bool is_equal = (true && ... && (previous[keys] == current[keys]));
227 return !is_equal;
228 }
229
230 concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
231 concurrent::FunctionId id, std::string_view name,
232 SnapshotEventSource::Function&& func);
233
234 concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
235 concurrent::FunctionId id, std::string_view name,
236 DiffEventSource::Function&& func);
237
238 impl::StorageData* storage_;
239};
240
241} // namespace dynamic_config
242
243USERVER_NAMESPACE_END