Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
ConfigDefinition.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2024, the clio developers.
5
6 Permission to use, copy, modify, and distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#pragma once
21
22#include "rpc/common/APIVersion.hpp"
23#include "util/Assert.hpp"
24#include "util/newconfig/Array.hpp"
25#include "util/newconfig/ConfigConstraints.hpp"
26#include "util/newconfig/ConfigFileInterface.hpp"
27#include "util/newconfig/ConfigValue.hpp"
28#include "util/newconfig/Error.hpp"
29#include "util/newconfig/ObjectView.hpp"
30#include "util/newconfig/Types.hpp"
31#include "util/newconfig/ValueView.hpp"
32
33#include <algorithm>
34#include <chrono>
35#include <cstddef>
36#include <cstdint>
37#include <initializer_list>
38#include <optional>
39#include <string>
40#include <string_view>
41#include <thread>
42#include <unordered_map>
43#include <utility>
44#include <variant>
45#include <vector>
46
47namespace util::config {
48
55public:
56 using KeyValuePair = std::pair<std::string_view, std::variant<ConfigValue, Array>>;
57
66 ClioConfigDefinition(std::initializer_list<KeyValuePair> pair);
67
77 [[nodiscard]] std::optional<std::vector<Error>>
78 parse(ConfigFileInterface const& config);
79
87 [[nodiscard]] ObjectView
88 getObject(std::string_view prefix, std::optional<std::size_t> idx = std::nullopt) const;
89
96 [[nodiscard]] ValueView
97 getValueView(std::string_view fullKey) const;
98
106 template <typename T>
107 T
108 get(std::string_view fullKey) const
109 {
110 ASSERT(map_.contains(fullKey), "key {} does not exist in config", fullKey);
111 auto const val = map_.at(fullKey);
112 if (std::holds_alternative<ConfigValue>(val)) {
113 return ValueView{std::get<ConfigValue>(val)}.getValueImpl<T>();
114 }
115 std::unreachable();
116 }
117
125 [[nodiscard]] ValueView
126 getValueInArray(std::string_view fullKey, std::size_t index) const;
127
134 [[nodiscard]] ArrayView
135 getArray(std::string_view prefix) const;
136
143 [[nodiscard]] bool
144 contains(std::string_view key) const;
145
152 [[nodiscard]] bool
153 hasItemsWithPrefix(std::string_view key) const;
154
161 [[nodiscard]] Array const&
162 asArray(std::string_view key) const;
163
170 [[nodiscard]] std::size_t
171 arraySize(std::string_view prefix) const;
172
179 static std::chrono::milliseconds
180 toMilliseconds(float value);
181
189 template <typename T>
190 std::optional<T>
191 maybeValue(std::string_view fullKey) const
192 {
193 return getValueView(fullKey).asOptional<T>();
194 }
195
201 [[nodiscard]] auto
202 begin() const
203 {
204 return map_.begin();
205 }
206
212 [[nodiscard]] auto
213 end() const
214 {
215 return map_.end();
216 }
217
218private:
225 [[nodiscard]] auto
226 getArrayIterator(std::string_view key) const
227 {
228 auto const fullKey = addBracketsForArrayKey(key);
229 auto const it = std::ranges::find_if(map_, [&fullKey](auto pair) { return pair.first == fullKey; });
230
231 ASSERT(it != map_.end(), "key {} does not exist in config", fullKey);
232 ASSERT(std::holds_alternative<Array>(it->second), "Value of {} is not an array", fullKey);
233
234 return it;
235 }
236
243 [[nodiscard]] static std::string
244 addBracketsForArrayKey(std::string_view key)
245 {
246 std::string fullKey = std::string(key);
247 if (!key.contains(".[]"))
248 fullKey += ".[]";
249 return fullKey;
250 }
251
252 std::unordered_map<std::string_view, std::variant<ConfigValue, Array>> map_;
253};
254
261static ClioConfigDefinition gClioConfig = ClioConfigDefinition{
262 {{"database.type", ConfigValue{ConfigType::String}.defaultValue("cassandra").withConstraint(gValidateCassandraName)
263 },
264 {"database.cassandra.contact_points", ConfigValue{ConfigType::String}.defaultValue("localhost")},
265 {"database.cassandra.secure_connect_bundle", ConfigValue{ConfigType::String}.optional()},
266 {"database.cassandra.port", ConfigValue{ConfigType::Integer}.withConstraint(gValidatePort).optional()},
267 {"database.cassandra.keyspace", ConfigValue{ConfigType::String}.defaultValue("clio")},
268 {"database.cassandra.replication_factor",
269 ConfigValue{ConfigType::Integer}.defaultValue(3u).withConstraint(gValidateUint16)},
270 {"database.cassandra.table_prefix", ConfigValue{ConfigType::String}.optional()},
271 {"database.cassandra.max_write_requests_outstanding",
272 ConfigValue{ConfigType::Integer}.defaultValue(10'000).withConstraint(gValidateUint32)},
273 {"database.cassandra.max_read_requests_outstanding",
274 ConfigValue{ConfigType::Integer}.defaultValue(100'000).withConstraint(gValidateUint32)},
275 {"database.cassandra.threads",
276 ConfigValue{ConfigType::Integer}
277 .defaultValue(static_cast<uint32_t>(std::thread::hardware_concurrency()))
278 .withConstraint(gValidateUint32)},
279 {"database.cassandra.core_connections_per_host",
280 ConfigValue{ConfigType::Integer}.defaultValue(1).withConstraint(gValidateUint16)},
281 {"database.cassandra.queue_size_io", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint16)},
282 {"database.cassandra.write_batch_size",
283 ConfigValue{ConfigType::Integer}.defaultValue(20).withConstraint(gValidateUint16)},
284 {"database.cassandra.connect_timeout", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)
285 },
286 {"database.cassandra.request_timeout", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)
287 },
288 {"database.cassandra.username", ConfigValue{ConfigType::String}.optional()},
289 {"database.cassandra.password", ConfigValue{ConfigType::String}.optional()},
290 {"database.cassandra.certfile", ConfigValue{ConfigType::String}.optional()},
291
292 {"allow_no_etl", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
293
294 {"etl_sources.[].ip", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidateIp)}},
295 {"etl_sources.[].ws_port", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidatePort)}},
296 {"etl_sources.[].grpc_port", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidatePort)}},
297
298 {"forwarding.cache_timeout",
299 ConfigValue{ConfigType::Double}.defaultValue(0.0).withConstraint(gValidatePositiveDouble)},
300 {"forwarding.request_timeout",
301 ConfigValue{ConfigType::Double}.defaultValue(10.0).withConstraint(gValidatePositiveDouble)},
302
303 {"rpc.cache_timeout", ConfigValue{ConfigType::Double}.defaultValue(0.0).withConstraint(gValidatePositiveDouble)},
304
305 {"num_markers", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateNumMarkers)},
306
307 {"dos_guard.whitelist.[]", Array{ConfigValue{ConfigType::String}.optional()}},
308 {"dos_guard.max_fetches", ConfigValue{ConfigType::Integer}.defaultValue(1000'000u).withConstraint(gValidateUint32)
309 },
310 {"dos_guard.max_connections", ConfigValue{ConfigType::Integer}.defaultValue(20u).withConstraint(gValidateUint32)},
311 {"dos_guard.max_requests", ConfigValue{ConfigType::Integer}.defaultValue(20u).withConstraint(gValidateUint32)},
312 {"dos_guard.sweep_interval",
313 ConfigValue{ConfigType::Double}.defaultValue(1.0).withConstraint(gValidatePositiveDouble)},
314
315 {"workers",
316 ConfigValue{ConfigType::Integer}.defaultValue(std::thread::hardware_concurrency()).withConstraint(gValidateUint32)
317 },
318
319 {"server.ip", ConfigValue{ConfigType::String}.withConstraint(gValidateIp)},
320 {"server.port", ConfigValue{ConfigType::Integer}.withConstraint(gValidatePort)},
321 {"server.max_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(0).withConstraint(gValidateUint32)},
322 {"server.local_admin", ConfigValue{ConfigType::Boolean}.optional()},
323 {"server.admin_password", ConfigValue{ConfigType::String}.optional()},
324 {"server.processing_policy",
325 ConfigValue{ConfigType::String}.defaultValue("parallel").withConstraint(gValidateProcessingPolicy)},
326 {"server.parallel_requests_limit", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint16)},
327 {"server.ws_max_sending_queue_size",
328 ConfigValue{ConfigType::Integer}.defaultValue(1500).withConstraint(gValidateUint32)},
329 {"server.__ng_web_server", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
330
331 {"prometheus.enabled", ConfigValue{ConfigType::Boolean}.defaultValue(true)},
332 {"prometheus.compress_reply", ConfigValue{ConfigType::Boolean}.defaultValue(true)},
333
334 {"io_threads", ConfigValue{ConfigType::Integer}.defaultValue(2).withConstraint(gValidateIOThreads)},
335
336 {"subscription_workers", ConfigValue{ConfigType::Integer}.defaultValue(1).withConstraint(gValidateUint32)},
337
338 {"graceful_period", ConfigValue{ConfigType::Double}.defaultValue(10.0).withConstraint(gValidatePositiveDouble)},
339
340 {"cache.num_diffs", ConfigValue{ConfigType::Integer}.defaultValue(32).withConstraint(gValidateUint16)},
341 {"cache.num_markers", ConfigValue{ConfigType::Integer}.defaultValue(48).withConstraint(gValidateUint16)},
342 {"cache.num_cursors_from_diff", ConfigValue{ConfigType::Integer}.defaultValue(0).withConstraint(gValidateUint16)},
343 {"cache.num_cursors_from_account", ConfigValue{ConfigType::Integer}.defaultValue(0).withConstraint(gValidateUint16)
344 },
345 {"cache.page_fetch_size", ConfigValue{ConfigType::Integer}.defaultValue(512).withConstraint(gValidateUint16)},
346 {"cache.load", ConfigValue{ConfigType::String}.defaultValue("async").withConstraint(gValidateLoadMode)},
347
348 {"log_channels.[].channel", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidateChannelName)}
349 },
350 {"log_channels.[].log_level",
351 Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidateLogLevelName)}},
352
353 {"log_level", ConfigValue{ConfigType::String}.defaultValue("info").withConstraint(gValidateLogLevelName)},
354
355 {"log_format",
356 ConfigValue{ConfigType::String}.defaultValue(
357 R"(%TimeStamp% (%SourceLocation%) [%ThreadID%] %Channel%:%Severity% %Message%)"
358 )},
359
360 {"log_to_console", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
361
362 {"log_directory", ConfigValue{ConfigType::String}.optional()},
363
364 {"log_rotation_size", ConfigValue{ConfigType::Integer}.defaultValue(2048).withConstraint(gValidateLogSize)},
365
366 {"log_directory_max_size",
367 ConfigValue{ConfigType::Integer}.defaultValue(50 * 1024).withConstraint(gValidateLogSize)},
368
369 {"log_rotation_hour_interval",
370 ConfigValue{ConfigType::Integer}.defaultValue(12).withConstraint(gValidateLogRotationTime)},
371
372 {"log_tag_style", ConfigValue{ConfigType::String}.defaultValue("none").withConstraint(gValidateLogTag)},
373
374 {"extractor_threads", ConfigValue{ConfigType::Integer}.defaultValue(1u).withConstraint(gValidateUint32)},
375
376 {"read_only", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
377
378 {"txn_threshold", ConfigValue{ConfigType::Integer}.defaultValue(0).withConstraint(gValidateUint16)},
379
380 {"start_sequence", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)},
381
382 {"finish_sequence", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)},
383
384 {"ssl_cert_file", ConfigValue{ConfigType::String}.optional()},
385
386 {"ssl_key_file", ConfigValue{ConfigType::String}.optional()},
387
388 {"api_version.default",
389 ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_DEFAULT).withConstraint(gValidateApiVersion)},
390 {"api_version.min",
391 ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MIN).withConstraint(gValidateApiVersion)},
392 {"api_version.max",
393 ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MAX).withConstraint(gValidateApiVersion)},
394
395 {"migration.full_scan_threads", ConfigValue{ConfigType::Integer}.defaultValue(2).withConstraint(gValidateUint32)},
396 {"migration.full_scan_jobs", ConfigValue{ConfigType::Integer}.defaultValue(4).withConstraint(gValidateUint32)},
397 {"migration.cursors_per_job", ConfigValue{ConfigType::Integer}.defaultValue(100).withConstraint(gValidateUint32)}},
398
399};
400
401} // namespace util::config
View for array structure for config.
Definition ArrayView.hpp:42
Array definition to store multiple values provided by the user from Json/Yaml.
Definition Array.hpp:41
All the config data will be stored and extracted from this class.
Definition ConfigDefinition.hpp:54
auto begin() const
Returns an iterator to the beginning of the configuration map.
Definition ConfigDefinition.hpp:202
std::size_t arraySize(std::string_view prefix) const
Returns the size of an Array.
Definition ConfigDefinition.cpp:143
ArrayView getArray(std::string_view prefix) const
Returns the specified Array object from ClioConfigDefinition.
Definition ConfigDefinition.cpp:82
static std::chrono::milliseconds toMilliseconds(float value)
Method to convert a float seconds value to milliseconds.
Definition ConfigDefinition.cpp:122
auto end() const
Returns an iterator to the end of the configuration map.
Definition ConfigDefinition.hpp:213
std::optional< std::vector< Error > > parse(ConfigFileInterface const &config)
Parses the configuration file.
Definition ConfigDefinition.cpp:157
bool contains(std::string_view key) const
Checks if a key is present in the configuration map.
Definition ConfigDefinition.cpp:99
ObjectView getObject(std::string_view prefix, std::optional< std::size_t > idx=std::nullopt) const
Returns the ObjectView specified with the prefix.
Definition ConfigDefinition.cpp:61
bool hasItemsWithPrefix(std::string_view key) const
Checks if any key in config starts with "key".
Definition ConfigDefinition.cpp:105
ClioConfigDefinition(std::initializer_list< KeyValuePair > pair)
Constructs a new ClioConfigDefinition.
Definition ConfigDefinition.cpp:51
Array const & asArray(std::string_view key) const
Returns the Array object associated with the specified key.
Definition ConfigDefinition.cpp:136
ValueView getValueView(std::string_view fullKey) const
Returns the specified ValueView object associated with the key.
Definition ConfigDefinition.cpp:111
std::optional< T > maybeValue(std::string_view fullKey) const
Returns the specified value of given string of type T if type and value exists.
Definition ConfigDefinition.hpp:191
T get(std::string_view fullKey) const
Returns the specified value of given string if value exists.
Definition ConfigDefinition.hpp:108
ValueView getValueInArray(std::string_view fullKey, std::size_t index) const
Returns the specified ValueView object in an array with a given index.
Definition ConfigDefinition.cpp:129
The interface for configuration files.
Definition ConfigFileInterface.hpp:35
Provides a view into a subset of configuration data defined by a prefix.
Definition ObjectView.hpp:40
Provides view into ConfigValues that represents values in Clio Config.
Definition ValueView.hpp:46
T getValueImpl() const
Retrieves the stored value as the specified type T.
Definition ValueView.hpp:165
std::optional< T > asOptional() const
Returns an optional value of the specified type T if valid.
Definition ValueView.hpp:193
static constexpr uint32_t kAPI_VERSION_MIN
Minimum API version supported by this build.
Definition APIVersion.hpp:38
static constexpr uint32_t kAPI_VERSION_MAX
Maximum API version supported by this build.
Definition APIVersion.hpp:43
static constexpr uint32_t kAPI_VERSION_DEFAULT
Default API version to use if no version is specified by clients.
Definition APIVersion.hpp:33