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