userver: userver/dynamic_config/snapshot.hpp Source File
Loading...
Searching...
No Matches
snapshot.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/dynamic_config/snapshot.hpp
4/// @brief @copybrief dynamic_config::Snapshot
5
6#include <cstdint>
7#include <string>
8#include <type_traits>
9
10#include <userver/compiler/impl/lifetime.hpp>
11#include <userver/dynamic_config/impl/snapshot.hpp>
12#include <userver/dynamic_config/impl/to_json.hpp>
13#include <userver/dynamic_config/registered_config_meta.hpp>
14#include <userver/formats/json_fwd.hpp>
15#include <userver/utils/fast_pimpl.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace dynamic_config {
20
21/// A strong typedef for usage in dynamic_config::Key constructors.
22struct DefaultAsJsonString final {
23 constexpr explicit DefaultAsJsonString(std::string_view json_string);
24
25 std::string_view json_string;
26};
27
28/// A config name-value pair for usage in dynamic_config::Key constructors.
29struct ConfigDefault final {
30 template <typename T>
31 ConfigDefault(std::string_view name, const T& value);
32
33 ConfigDefault(std::string_view name, DefaultAsJsonString default_json);
34
35 std::string_view name;
36 std::string default_json;
37};
38
39/// A tag type for usage in dynamic_config::Key constructors.
40struct ConstantConfig final {
41 constexpr explicit ConstantConfig() = default;
42};
43
44/// @brief A config key is a unique identifier for a config variable
45/// @snippet core/src/dynamic_config/config_test.cpp key bool
46template <typename Variable>
47class Key final {
48public:
49 /// The type of the parsed config variable.
50 using VariableType = Variable;
51
52 using JsonParser = Variable (*)(const formats::json::Value&);
53 using DocsMapParser = Variable (*)(const DocsMap&);
54
55 /// @brief The constructor for a trivial `VariableType`, e.g. `bool`, integer,
56 /// `double`, `string`. The default is passed by value.
57 ///
58 /// Usage example:
59 /// @snippet core/src/dynamic_config/config_test.cpp key bool
60 Key(std::string_view name, const VariableType& default_value);
61
62 /// @brief The constructor for a non-trivial `VariableType`. The default is
63 /// passed as a JSON string.
64 ///
65 /// Uses formats::json::Value `Parse` customization point function to parse
66 /// `VariableType`.
67 ///
68 /// Usage example:
69 /// @snippet core/src/dynamic_config/config_test.cpp struct config cpp
70 Key(std::string_view name, DefaultAsJsonString default_json);
71
72 /// @brief The constructor that provides a special parser from JSON.
73 /// @warning Prefer the constructors above whenever possible.
74 /// @details Can be used when generic `Parse` is not applicable. Sometimes
75 /// used to add validation, e.g. minimum, maximum, string pattern, etc.
76 Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json);
77
78 /// @brief Constructor with a schema_hash, used by chaotic codegen.
79 /// @param schema_hash SHA-256(canonical JSON of schema with inlined definitions),
80 /// lowercase hex, 64 chars. Stored in the global registry and
81 /// retrievable via dynamic_config::impl::GetRegisteredConfigsMeta()
82 /// as dynamic_config::RegisteredConfigMeta::schema_hash.
83 /// @warning This constructor is intended for use by chaotic codegen only,
84 /// not for manual calls. Backwards-incompatible changes may be
85 /// introduced in the future, e.g. adding additional parameters.
86 Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json, std::string_view schema_hash);
87
88 /// @brief The constructor that parses multiple JSON config items
89 /// into a single C++ object.
90 /// @warning Prefer to use a separate `Key` per JSON config item and use the
91 /// constructors above whenever possible.
92 template <std::size_t N>
93 Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N]);
94
95 /// Creates a config that always has the same value.
96 Key(ConstantConfig, VariableType value);
97
98 /// @cond
99 Key(impl::InternalTag, std::string_view name);
100
101 Key(impl::InternalTag, DocsMapParser parser);
102 /// @endcond
103
104 Key(const Key&) noexcept = delete;
105 Key& operator=(const Key&) noexcept = delete;
106
107 /// @returns the name of the config variable, as passed at the construction.
108 std::string_view GetName() const noexcept;
109
110 /// Parses the config. Useful only in some very niche scenarios. The config
111 /// value should be typically be retrieved from dynamic_config::Snapshot,
112 /// which is obtained from components::DynamicConfig in production or from
113 /// dynamic_config::StorageMock in unit tests.
114 VariableType Parse(const DocsMap& docs_map) const;
115
116private:
117 friend struct impl::ConfigIdGetter;
118
119 const impl::ConfigId id_;
120};
121
122/// @brief The shared snapshot of
123/// @ref scripts/docs/en/userver/dynamic_config.md "dynamic configs". Cheap to
124/// copy, even cheaper to move. Thread safe, not updated with new dynamic
125/// config values in background (it's a snapshot!).
126///
127/// When a config update comes in via new `DocsMap`, configs of all
128/// the registered types are constructed and stored in `Config`. After that
129/// the `DocsMap` is dropped.
130///
131/// Config types are automatically registered if they are used
132/// somewhere in the program.
133///
134/// ## Usage example:
135/// @snippet components/component_sample_test.cpp Sample user component runtime config source
136class Snapshot final {
137public:
138 Snapshot(const Snapshot&);
139 Snapshot& operator=(const Snapshot&);
140
141 Snapshot(Snapshot&&) noexcept;
142 Snapshot& operator=(Snapshot&&) noexcept;
143
144 ~Snapshot();
145
146 /// Used to access individual configs in the type-safe config map
147 template <typename VariableType>
148 const VariableType& operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND;
149
150 /// Used to access individual configs in the type-safe config map
151 template <typename VariableType>
152 const VariableType& operator[](const Key<VariableType>&) &&;
153
154private:
155 // for the constructor
156 friend class Source;
157 friend class impl::StorageData;
158 friend struct Diff;
159
160 explicit Snapshot(const impl::StorageData& storage);
161
162 const impl::SnapshotData& GetData() const;
163
164 struct Impl;
165 utils::FastPimpl<Impl, 16, 8> impl_;
166};
167
168// ========================== Implementation follows ==========================
169
170constexpr DefaultAsJsonString::DefaultAsJsonString(std::string_view json_string)
171 : json_string(json_string)
172{}
173
174template <typename T>
175ConfigDefault::ConfigDefault(std::string_view name, const T& value)
176 : name(name),
177 default_json(impl::ToJsonString(value))
178{}
179
180template <typename Variable>
181Key<Variable>::Key(std::string_view name, const VariableType& default_value)
182 : id_(impl::Register(
183 std::string{name},
184 [name = std::string{name}](const auto& docs_map) -> std::any {
185 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
186 },
187 impl::ValueToDocsMapString(name, default_value)
188 ))
189{}
190
191template <typename Variable>
192Key<Variable>::Key(std::string_view name, DefaultAsJsonString default_json)
193 : id_(impl::Register(
194 std::string{name},
195 [name = std::string{name}](const auto& docs_map) -> std::any {
196 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
197 },
198 impl::SingleToDocsMapString(name, default_json.json_string)
199 ))
200{}
201
202template <typename Variable>
203Key<Variable>::Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json)
204 : id_(impl::Register(
205 std::string{name},
206 [name = std::string{name}, parser](const auto& docs_map) -> std::any {
207 return parser(impl::DocsMapGet(docs_map, name));
208 },
209 impl::SingleToDocsMapString(name, default_json.json_string)
210 ))
211{}
212
213template <typename Variable>
214Key<Variable>::Key(
215 std::string_view name,
216 JsonParser parser,
217 DefaultAsJsonString default_json,
218 std::string_view schema_hash
219)
220 : id_(impl::Register(
221 std::string{name},
222 [name = std::string{name}, parser](const auto& docs_map) -> std::any {
223 return parser(impl::DocsMapGet(docs_map, name));
224 },
225 impl::SingleToDocsMapString(name, default_json.json_string),
226 std::string{schema_hash}
227 ))
228{}
229
230template <typename Variable>
231template <std::size_t N>
232Key<Variable>::Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N])
233 : id_(impl::Register(
234 std::string{},
235 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
236 impl::MultipleToDocsMapString(default_json_map, N)
237 ))
238{}
239
240template <typename Variable>
241Key<Variable>::Key(ConstantConfig /*tag*/, VariableType value)
242 : id_(impl::Register(std::string{}, [value = std::move(value)](const DocsMap& /*unused*/) { return value; }, "{}"))
243{}
244
245template <typename Variable>
246Key<Variable>::Key(impl::InternalTag, std::string_view name)
247 : id_(impl::Register(
248 std::string{name},
249 [name = std::string{name}](const auto& docs_map) -> std::any {
250 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
251 },
252 "{}"
253 ))
254{}
255
256template <typename Variable>
257Key<Variable>::Key(impl::InternalTag, DocsMapParser parser)
258 : id_(impl::Register(
259 std::string{},
260 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
261 "{}"
262 ))
263{}
264
265template <typename VariableType>
266std::string_view Key<VariableType>::GetName() const noexcept {
267 return impl::GetName(id_);
268}
269
270template <typename VariableType>
271VariableType Key<VariableType>::Parse(const DocsMap& docs_map) const {
272 return std::any_cast<VariableType>(impl::MakeConfig(id_, docs_map));
273}
274
275template <typename VariableType>
276const VariableType& Snapshot::operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND {
277 return GetData().Get<VariableType>(impl::ConfigIdGetter::Get(key));
278}
279
280template <typename VariableType>
281const VariableType& Snapshot::operator[](const Key<VariableType>&) && {
282 static_assert(!sizeof(VariableType), "keep the Snapshot before using, please");
283}
284
285} // namespace dynamic_config
286
287USERVER_NAMESPACE_END