Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
ConfigDescription.hpp
1#pragma once
2
3#include "util/Assert.hpp"
4#include "util/config/ConfigDefinition.hpp"
5#include "util/config/Error.hpp"
6
7#include <fmt/format.h>
8
9#include <algorithm>
10#include <array>
11#include <cerrno>
12#include <cstring>
13#include <expected>
14#include <filesystem>
15#include <fstream>
16#include <iostream>
17#include <string_view>
18
19namespace util::config {
20
27public:
31 struct KV {
32 std::string_view key;
33 std::string_view value;
34 };
35
42 constexpr ClioConfigDescription() = default;
43
50 [[nodiscard]] static constexpr std::string_view
51 get(std::string_view key)
52 {
53 auto const itr =
54 std::ranges::find_if(kConfigDescription, [&](auto const& v) { return v.key == key; });
55 ASSERT(itr != kConfigDescription.end(), "Key {} doesn't exist in config", key);
56 return itr->value;
57 }
58
65 [[nodiscard]] static std::expected<void, Error>
66 generateConfigDescriptionToFile(std::filesystem::path path)
67 {
68 namespace fs = std::filesystem;
69
70 // Validate the directory exists
71 auto const dir = path.parent_path();
72 if (!dir.empty() && !fs::exists(dir)) {
73 return std::unexpected<Error>{fmt::format(
74 "Error: Directory '{}' does not exist or provided path is invalid", dir.string()
75 )};
76 }
77
78 std::ofstream file(path.string());
79 if (!file.is_open()) {
80 return std::unexpected{
81 fmt::format("Failed to create file '{}': {}", path.string(), std::strerror(errno))
82 };
83 }
84
86 file.close();
87
88 std::cout << "Markdown file generated successfully: " << path << "\n";
89 return {};
90 }
91
97 static void
98 writeConfigDescriptionToFile(std::ostream& file)
99 {
100 file << kConfigDescriptionHeader;
101
102 for (auto const& [key, val] : kConfigDescription) {
103 file << "\n### " << key << "\n\n";
104
105 // Every type of value is directed to operator<< in ConfigValue.hpp
106 // as ConfigValue is the one that holds all the info regarding the config values
107 if (key.contains("[]")) {
108 file << getClioConfig().asArray(key);
109 } else {
110 file << getClioConfig().getValueView(key);
111 }
112 file << "- **Description**: " << val << "\n";
113 }
114 }
115
116private:
117 static constexpr auto kConfigDescriptionHeader =
118 R"(# Clio Config Description
119
120This document provides a list of all available Clio configuration properties in detail.
121
122> [!NOTE]
123> Dot notation in configuration key names represents nested fields.
124> For example, **database.scylladb** refers to the _scylladb_ field inside the _database_ object.
125> If a key name includes "[]", it indicates that the nested field is an array (e.g., etl_sources.[]).
126
127## Configuration Details
128)";
129
130 static constexpr auto kConfigDescription = std::array{
131 KV{.key = "database.type",
132 .value = "Specifies the type of database used for storing and retrieving data required "
133 "by the Clio server. Both "
134 "ScyllaDB and Cassandra can serve as backends for Clio; however, this value "
135 "must be set to `cassandra`."},
136 KV{.key = "database.cassandra.contact_points",
137 .value = "A list of IP addresses or hostnames for the initial cluster nodes (Cassandra "
138 "or ScyllaDB) that "
139 "the client connects to when establishing a database connection. If you're "
140 "running Clio locally, "
141 "set this value to `localhost` or `127.0.0.1`."},
142 KV{.key = "database.cassandra.secure_connect_bundle",
143 .value = "The configuration file that contains the necessary credentials and connection "
144 "details for "
145 "securely connecting to a Cassandra database cluster."},
146 KV{.key = "database.cassandra.port",
147 .value = "The port number used to connect to the Cassandra database."},
148 KV{.key = "database.cassandra.keyspace",
149 .value = "The Cassandra keyspace to use for the database. If you don't provide a value, "
150 "this is set to "
151 "`clio` by default."},
152 KV{.key = "database.cassandra.replication_factor",
153 .value =
154 "Represents the number of replicated nodes for ScyllaDB. For more details see "
155 "[Fault Tolerance "
156 "Replication "
157 "Factor](https://university.scylladb.com/courses/scylla-essentials-overview/lessons/"
158 "high-availability/topic/fault-tolerance-replication-factor/)."},
159 KV{.key = "database.cassandra.table_prefix",
160 .value =
161 "An optional field to specify a prefix for the Cassandra database table names."},
162 KV{.key = "database.cassandra.max_write_requests_outstanding",
163 .value = "Represents the maximum number of outstanding write requests. Write requests "
164 "are API calls that "
165 "write to the database."},
166 KV{.key = "database.cassandra.max_read_requests_outstanding",
167 .value = "Maximum number of outstanding read requests. Read requests are API calls that "
168 "read from the database."},
169 KV{.key = "database.cassandra.threads",
170 .value = "Represents the number of threads that will be used for database operations."},
171 KV{.key = "database.cassandra.provider",
172 .value = "The specific database backend provider we are using."},
173 KV{.key = "database.cassandra.core_connections_per_host",
174 .value = "The number of core connections per host for the Cassandra database."},
175 KV{.key = "database.cassandra.queue_size_io",
176 .value = "Defines the queue size of the input/output (I/O) operations in Cassandra."},
177 KV{.key = "database.cassandra.write_batch_size",
178 .value = "Represents the batch size for write operations in Cassandra."},
179 KV{.key = "database.cassandra.connect_timeout",
180 .value = "The maximum amount of time in seconds that the system waits for a database "
181 "connection to be "
182 "established."},
183 KV{.key = "database.cassandra.request_timeout",
184 .value = "The maximum amount of time in seconds that the system waits for a request to "
185 "be fetched from the database. Should be set higher than the server side read "
186 "timeout. If omitted, no request timeout is applied."},
187 KV{.key = "database.cassandra.initial_request_retry_delay",
188 .value = "How long in seconds to wait before the first retry of a database request "
189 "that failed with a transient error."},
190 KV{.key = "database.cassandra.max_request_retry_delay",
191 .value = "Upper bound in seconds for the exponential backoff between retries of a "
192 "database request."},
193 KV{.key = "database.cassandra.username",
194 .value = "The username used for authenticating with the database."},
195 KV{.key = "database.cassandra.password",
196 .value = "The password used for authenticating with the database."},
197 KV{.key = "database.cassandra.certfile",
198 .value = "The path to the SSL/TLS certificate file used to establish a secure "
199 "connection between the client "
200 "and the Cassandra database."},
201 KV{.key = "allow_no_etl",
202 .value = "If set to `True`, allows Clio to start without any ETL source."},
203 KV{.key = "etl_sources.[].ip", .value = "The IP address of the ETL source."},
204 KV{.key = "etl_sources.[].ws_port", .value = "The WebSocket port of the ETL source."},
205 KV{.key = "etl_sources.[].grpc_port", .value = "The gRPC port of the ETL source."},
206 KV{.key = "forwarding.cache_timeout",
207 .value = "Specifies the timeout duration (in seconds) for the forwarding cache used in "
208 "`rippled` "
209 "communication. A value of `0` means disabling this feature."},
210 KV{.key = "forwarding.request_timeout",
211 .value = "Specifies the timeout duration (in seconds) for the forwarding request used "
212 "in `rippled` "
213 "communication."},
214 KV{.key = "rpc.cache_timeout",
215 .value = "Specifies the timeout duration (in seconds) for RPC cache response to "
216 "timeout. A value of `0` "
217 "means disabling this feature."},
218 KV{.key = "num_markers",
219 .value = "Specifies the number of coroutines used to download the initial ledger."},
220 KV{.key = "dos_guard.whitelist.[]",
221 .value = "The list of IP addresses to whitelist for DOS protection."},
222 KV{.key = "dos_guard.max_fetches",
223 .value = "The maximum number of fetch operations allowed by DOS guard."},
224 KV{.key = "dos_guard.max_connections",
225 .value = "The maximum number of concurrent connections for a specific IP address."},
226 KV{.key = "dos_guard.max_requests",
227 .value = "The maximum number of requests allowed for a specific IP address."},
228 KV{.key = "dos_guard.sweep_interval",
229 .value = "Interval in seconds for DOS guard to sweep(clear) its state."},
230 KV{.key = "workers", .value = "The number of threads used to process RPC requests."},
231 KV{.key = "server.ip", .value = "The IP address of the Clio HTTP server."},
232 KV{.key = "server.port", .value = "The port number of the Clio HTTP server."},
233 KV{.key = "server.max_queue_size",
234 .value = "The maximum size of the server's request queue. If set to `0`, this means "
235 "there is no queue size "
236 "limit."},
237 KV{.key = "server.local_admin",
238 .value = "Indicates if requests from `localhost` are allowed to call Clio admin-only "
239 "APIs. Note that this "
240 "setting cannot be enabled "
241 "together with [server.admin_password](#serveradmin_password)."},
242 KV{.key = "server.admin_password",
243 .value = "The password for Clio admin-only APIs. Note that this setting cannot be "
244 "enabled together with "
245 "[server.local_admin](#serveradmin_password)."},
246 KV{.key = "server.processing_policy",
247 .value = "For the `sequent` policy, requests from a single client connection are "
248 "processed one by one, with "
249 "the next request read only after the previous one is processed. For the "
250 "`parallel` policy, Clio "
251 "will accept all requests and process them in parallel, sending a reply for "
252 "each request as soon "
253 "as it is ready."},
254 KV{.key = "server.parallel_requests_limit",
255 .value = "This is an optional parameter, used only if the `processing_strategy` is "
256 "`parallel`. It limits "
257 "the number of requests processed in parallel for a single client connection. "
258 "If not specified, no "
259 "limit is enforced."},
260 KV{.key = "server.ws_max_sending_queue_size",
261 .value = "Maximum queue size for sending subscription data to clients. This queue "
262 "buffers data when a "
263 "client is slow to receive it, ensuring delivery once the client is ready."},
264 KV{.key = "server.proxy.ips.[]",
265 .value =
266 "List of proxy ip addresses. When Clio receives a request from proxy it will use "
267 "`Forwarded` value (if any) as client ip. When this option is used together with "
268 "`server.proxy.tokens` Clio will identify proxy by ip or by token."},
269 KV{.key = "server.proxy.tokens.[]",
270 .value = "List of tokens in identifying request as a request from proxy. Token should "
271 "be provided in "
272 "`X-Proxy-Token` header, e.g. "
273 "`X-Proxy-Token: <very_secret_token>'. When Clio receives a request from proxy "
274 "it will use 'Forwarded` value (if any) to get client ip. When this option is "
275 "used together with "
276 "'server.proxy.ips' Clio will identify proxy by ip or by token."},
277 KV{.key = "prometheus.enabled", .value = "Enables or disables Prometheus metrics."},
278 KV{.key = "prometheus.compress_reply",
279 .value = "Enables or disables compression of Prometheus responses."},
280 KV{.key = "io_threads",
281 .value = "The number of input/output (I/O) threads. The value cannot be less than `1`."},
282 KV{.key = "subscription_workers",
283 .value = "The number of worker threads or processes that are responsible for managing "
284 "and processing "
285 "subscription-based tasks from `rippled`."},
286 KV{.key = "graceful_period",
287 .value = "The number of seconds the server waits to shutdown gracefully. If Clio does "
288 "not shutdown "
289 "gracefully after the specified value, it will be killed instead."},
290 KV{.key = "cache.num_diffs",
291 .value = "The number of cursors generated is the number of changed (without counting "
292 "deleted) objects in "
293 "the latest `cache.num_diffs` number of ledgers. Cursors are workers that load "
294 "the ledger cache "
295 "from the position of markers concurrently. For more information, please read "
296 "[README.md](../src/etl/README.md)."},
297 KV{.key = "cache.num_markers",
298 .value = "Specifies how many markers are placed randomly within the cache. These "
299 "markers define the "
300 "positions on the ledger that will be loaded concurrently by the workers. The "
301 "higher the number, "
302 "the more places within the cache we potentially cover."},
303 KV{.key = "cache.num_cursors_from_diff",
304 .value = "`cache.num_cursors_from_diff` number of cursors are generated by looking at "
305 "the number of changed "
306 "objects in the most recent ledger. If number of changed objects in current "
307 "ledger is not enough, "
308 "it will keep reading previous ledgers until it hit "
309 "`cache.num_cursors_from_diff`. If set to `0`, "
310 "the system defaults to generating cursors based on `cache.num_diffs`."},
311 KV{.key = "cache.num_cursors_from_account",
312 .value = "`cache.num_cursors_from_diff` of cursors are generated by reading accounts in "
313 "`account_tx` table. "
314 "If set to `0`, the system defaults to generating cursors based on "
315 "`cache.num_diffs`."},
316 KV{.key = "cache.page_fetch_size",
317 .value = "The number of ledger objects to fetch concurrently per marker."},
318 KV{.key = "cache.limit_load_in_cluster",
319 .value = "If enabled only one clio node in a cluster (sharing the same database) will "
320 "load cache at a time"},
321 KV{.key = "cache.load", .value = "The strategy used for Cache loading."},
322 KV{.key = "cache.file.path",
323 .value = "The path to a file where cache will be saved to on shutdown and loaded from "
324 "on startup. "
325 "If the file couldn't be read Clio will load cache as usual (from DB or from "
326 "rippled)."},
327 KV{.key = "cache.file.max_sequence_age",
328 .value = "Max allowed difference between the latest sequence in DB and in cache file. "
329 "If the cache file is "
330 "too old (contains too low latest sequence) Clio will reject using it."},
331 KV{.key = "cache.file.async_save",
332 .value =
333 "When false, Clio waits for cache saving to finish before shutting down. When true, "
334 "cache saving runs in parallel with other shutdown operations."},
335 KV{.key = "log.channels.[].channel", .value = "The name of the log channel."},
336 KV{.key = "log.channels.[].level", .value = "The log level for the specific log channel."},
337 KV{.key = "log.level",
338 .value = "The general logging level of Clio. This level is applied to all log channels "
339 "that do not have an "
340 "explicitly defined logging level."},
341 KV{.key = "log.format",
342 .value = R"(The format string for log messages using spdlog format patterns.
343
344Each of the variables expands like so:
345
346- `%Y-%m-%d %H:%M:%S.%f`: The full date and time of the log entry with microsecond precision
347- `%^`: Start color range
348- `%3!l`: The severity (aka log level) the entry was sent at stripped to 3 characters
349- `%n`: The logger name (channel) that this log entry was sent to
350- `%$`: End color range
351- `%v`: The actual log message
352
353Some additional variables that might be useful:
354
355- `%@`: A partial path to the C++ file and the line number in the said file (`src/file/path:linenumber`)
356- `%t`: The ID of the thread the log entry is written from
357
358Documentation can be found at: <https://github.com/gabime/spdlog/wiki/Custom-formatting>.)"},
359 KV{.key = "log.is_async", .value = "Whether spdlog is asynchronous or not."},
360 KV{.key = "log.enable_console", .value = "Enables or disables logging to the console."},
361 KV{.key = "log.directory", .value = "The directory path for the log files."},
362 KV{.key = "log.rotation_size",
363 .value = "The log rotation size in megabytes. When the log file reaches this particular "
364 "size, a new log "
365 "file starts."},
366 KV{.key = "log.directory_max_files",
367 .value = "The maximum number of log files in the directory."},
368 KV{.key = "log.rotate",
369 .value = "Enables or disables log file rotation. When disabled, a single log file is "
370 "used without size-based rotation. Useful when rotation is managed externally "
371 "(e.g., via logrotate)."},
372 KV{.key = "log.tag_style",
373 .value = "Log tags are unique identifiers for log messages. `uint`/`int` starts logging "
374 "from 0 and increments, "
375 "making it faster. In contrast, `uuid` generates a random unique identifier, "
376 "which adds overhead."},
377 KV{.key = "extractor_threads",
378 .value = "Number of threads used to extract data from ETL source."},
379 KV{.key = "read_only",
380 .value = "Indicates if the server is allowed to write data to the database."},
381 KV{.key = "start_sequence",
382 .value = "If specified, the ledger index Clio will start writing to the database from."},
383 KV{.key = "finish_sequence",
384 .value = "If specified, the final ledger that Clio will write to the database."},
385 KV{.key = "ssl_cert_file", .value = "The path to the SSL certificate file."},
386 KV{.key = "ssl_key_file", .value = "The path to the SSL key file."},
387 KV{.key = "api_version.default",
388 .value = "The default API version that the Clio server will run on."},
389 KV{.key = "api_version.min", .value = "The minimum API version allowed to use."},
390 KV{.key = "api_version.max", .value = "The maximum API version allowed to use."},
391 KV{.key = "migration.full_scan_threads",
392 .value = "The number of threads used to scan the table."},
393 KV{.key = "migration.full_scan_jobs",
394 .value = "The number of coroutines used to scan the table."},
395 KV{.key = "migration.cursors_per_job", .value = "The number of cursors each job will scan."}
396 };
397};
398
399} // namespace util::config
Struct to represent a key-value pair.
Definition ConfigDescription.hpp:31
static std::expected< void, Error > generateConfigDescriptionToFile(std::filesystem::path path)
Generate markdown file of all the clio config descriptions.
Definition ConfigDescription.hpp:66
static constexpr std::string_view get(std::string_view key)
Retrieves the description for a given key.
Definition ConfigDescription.hpp:51
static void writeConfigDescriptionToFile(std::ostream &file)
Writes to Config description to file.
Definition ConfigDescription.hpp:98
constexpr ClioConfigDescription()=default
Constructs a new Clio Config Description based on pre-existing descriptions.