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(gValidateReplicationFactor)},
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(
278 static_cast<uint32_t>(std::thread::hardware_concurrency()),
279 "The number of available CPU cores."
280 )
281 .withConstraint(gValidateUint32)},
282 {"database.cassandra.core_connections_per_host",
283 ConfigValue{ConfigType::Integer}.defaultValue(1).withConstraint(gValidateUint16)},
284 {"database.cassandra.queue_size_io", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint16)},
285 {"database.cassandra.write_batch_size",
286 ConfigValue{ConfigType::Integer}.defaultValue(20).withConstraint(gValidateUint16)},
287 {"database.cassandra.connect_timeout", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)
288 },
289 {"database.cassandra.request_timeout", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)
290 },
291 {"database.cassandra.username", ConfigValue{ConfigType::String}.optional()},
292 {"database.cassandra.password", ConfigValue{ConfigType::String}.optional()},
293 {"database.cassandra.certfile", ConfigValue{ConfigType::String}.optional()},
294
295 {"allow_no_etl", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
296 {"__ng_etl", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
297 {"etl_sources.[].ip", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidateIp)}},
298 {"etl_sources.[].ws_port", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidatePort)}},
299 {"etl_sources.[].grpc_port", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidatePort)}},
300
301 {"forwarding.cache_timeout",
302 ConfigValue{ConfigType::Double}.defaultValue(0.0).withConstraint(gValidatePositiveDouble)},
303 {"forwarding.request_timeout",
304 ConfigValue{ConfigType::Double}.defaultValue(10.0).withConstraint(gValidatePositiveDouble)},
305
306 {"rpc.cache_timeout", ConfigValue{ConfigType::Double}.defaultValue(0.0).withConstraint(gValidatePositiveDouble)},
307
308 {"num_markers", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateNumMarkers)},
309
310 {"dos_guard.whitelist.[]", Array{ConfigValue{ConfigType::String}.optional()}},
311 {"dos_guard.max_fetches", ConfigValue{ConfigType::Integer}.defaultValue(1000'000u).withConstraint(gValidateUint32)
312 },
313 {"dos_guard.max_connections", ConfigValue{ConfigType::Integer}.defaultValue(20u).withConstraint(gValidateUint32)},
314 {"dos_guard.max_requests", ConfigValue{ConfigType::Integer}.defaultValue(20u).withConstraint(gValidateUint32)},
315 {"dos_guard.sweep_interval",
316 ConfigValue{ConfigType::Double}.defaultValue(1.0).withConstraint(gValidatePositiveDouble)},
317
318 {"workers",
319 ConfigValue{ConfigType::Integer}
320 .defaultValue(std::thread::hardware_concurrency(), "The number of available CPU cores.")
321 .withConstraint(gValidateUint32)},
322 {"server.ip", ConfigValue{ConfigType::String}.withConstraint(gValidateIp)},
323 {"server.port", ConfigValue{ConfigType::Integer}.withConstraint(gValidatePort)},
324 {"server.max_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(1).withConstraint(gValidateUint32)},
325 {"server.local_admin", ConfigValue{ConfigType::Boolean}.optional()},
326 {"server.admin_password", ConfigValue{ConfigType::String}.optional()},
327 {"server.processing_policy",
328 ConfigValue{ConfigType::String}.defaultValue("parallel").withConstraint(gValidateProcessingPolicy)},
329 {"server.parallel_requests_limit", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint16)},
330 {"server.ws_max_sending_queue_size",
331 ConfigValue{ConfigType::Integer}.defaultValue(1500).withConstraint(gValidateUint32)},
332 {"server.__ng_web_server", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
333
334 {"prometheus.enabled", ConfigValue{ConfigType::Boolean}.defaultValue(true)},
335 {"prometheus.compress_reply", ConfigValue{ConfigType::Boolean}.defaultValue(true)},
336
337 {"io_threads", ConfigValue{ConfigType::Integer}.defaultValue(2).withConstraint(gValidateUint16)},
338
339 {"subscription_workers", ConfigValue{ConfigType::Integer}.defaultValue(1).withConstraint(gValidateUint32)},
340
341 {"graceful_period", ConfigValue{ConfigType::Double}.defaultValue(10.0).withConstraint(gValidatePositiveDouble)},
342
343 {"cache.num_diffs", ConfigValue{ConfigType::Integer}.defaultValue(32).withConstraint(gValidateUint16)},
344 {"cache.num_markers", ConfigValue{ConfigType::Integer}.defaultValue(48).withConstraint(gValidateUint16)},
345 {"cache.num_cursors_from_diff",
346 ConfigValue{ConfigType::Integer}.defaultValue(0).withConstraint(gValidateNumCursors)},
347 {"cache.num_cursors_from_account",
348 ConfigValue{ConfigType::Integer}.defaultValue(0).withConstraint(gValidateNumCursors)},
349 {"cache.page_fetch_size", ConfigValue{ConfigType::Integer}.defaultValue(512).withConstraint(gValidateUint16)},
350 {"cache.load", ConfigValue{ConfigType::String}.defaultValue("async").withConstraint(gValidateLoadMode)},
351
352 {"log_channels.[].channel", Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidateChannelName)}
353 },
354 {"log_channels.[].log_level",
355 Array{ConfigValue{ConfigType::String}.optional().withConstraint(gValidateLogLevelName)}},
356
357 {"log_level", ConfigValue{ConfigType::String}.defaultValue("info").withConstraint(gValidateLogLevelName)},
358
359 {"log_format",
360 ConfigValue{ConfigType::String}.defaultValue(
361 R"(%TimeStamp% (%SourceLocation%) [%ThreadID%] %Channel%:%Severity% %Message%)"
362 )},
363
364 {"log_to_console", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
365
366 {"log_directory", ConfigValue{ConfigType::String}.optional()},
367
368 {"log_rotation_size", ConfigValue{ConfigType::Integer}.defaultValue(2048).withConstraint(gValidateUint32)},
369
370 {"log_directory_max_size", ConfigValue{ConfigType::Integer}.defaultValue(50 * 1024).withConstraint(gValidateUint32)
371 },
372
373 {"log_rotation_hour_interval", ConfigValue{ConfigType::Integer}.defaultValue(12).withConstraint(gValidateUint32)},
374
375 {"log_tag_style", ConfigValue{ConfigType::String}.defaultValue("none").withConstraint(gValidateLogTag)},
376
377 {"extractor_threads", ConfigValue{ConfigType::Integer}.defaultValue(1u).withConstraint(gValidateUint32)},
378
379 {"read_only", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
380
381 {"start_sequence", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)},
382
383 {"finish_sequence", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint32)},
384
385 {"ssl_cert_file", ConfigValue{ConfigType::String}.optional()},
386
387 {"ssl_key_file", ConfigValue{ConfigType::String}.optional()},
388
389 {"api_version.default",
390 ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_DEFAULT).withConstraint(gValidateApiVersion)},
391 {"api_version.min",
392 ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MIN).withConstraint(gValidateApiVersion)},
393 {"api_version.max",
394 ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MAX).withConstraint(gValidateApiVersion)},
395
396 {"migration.full_scan_threads", ConfigValue{ConfigType::Integer}.defaultValue(2).withConstraint(gValidateUint32)},
397 {"migration.full_scan_jobs", ConfigValue{ConfigType::Integer}.defaultValue(4).withConstraint(gValidateUint32)},
398 {"migration.cursors_per_job", ConfigValue{ConfigType::Integer}.defaultValue(100).withConstraint(gValidateUint32)}},
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:144
ArrayView getArray(std::string_view prefix) const
Returns the specified Array object from ClioConfigDefinition.
Definition ConfigDefinition.cpp:83
static std::chrono::milliseconds toMilliseconds(float value)
Method to convert a float seconds value to milliseconds.
Definition ConfigDefinition.cpp:123
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:158
bool contains(std::string_view key) const
Checks if a key is present in the configuration map.
Definition ConfigDefinition.cpp:100
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:62
bool hasItemsWithPrefix(std::string_view key) const
Checks if any key in config starts with "key".
Definition ConfigDefinition.cpp:106
ClioConfigDefinition(std::initializer_list< KeyValuePair > pair)
Constructs a new ClioConfigDefinition.
Definition ConfigDefinition.cpp:52
Array const & asArray(std::string_view key) const
Returns the Array object associated with the specified key.
Definition ConfigDefinition.cpp:137
ValueView getValueView(std::string_view fullKey) const
Returns the specified ValueView object associated with the key.
Definition ConfigDefinition.cpp:112
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:130
The interface for configuration files.
Definition ConfigFileInterface.hpp:36
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