userver: userver/storages/postgres/options.hpp Source File
Loading...
Searching...
No Matches
options.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/options.hpp
4/// @brief Options
5
6#include <chrono>
7#include <cstdint>
8#include <iosfwd>
9#include <optional>
10#include <string>
11#include <unordered_map>
12#include <unordered_set>
13
14#include <userver/congestion_control/controllers/linear.hpp>
15#include <userver/storages/postgres/postgres_fwd.hpp>
16#include <userver/utils/impl/transparent_hash.hpp>
17#include <userver/utils/str_icase.hpp>
18#include <userver/utils/string_literal.hpp>
19
20USERVER_NAMESPACE_BEGIN
21
22namespace storages::postgres {
23
24/*! [Isolation levels] */
25/// @brief SQL transaction isolation level
26/// @see https://www.postgresql.org/docs/current/static/sql-set-transaction.html
28 kReadCommitted, //!< READ COMMITTED
29 kRepeatableRead, //!< REPEATABLE READ
30 kSerializable, //!< SERIALIZABLE
31 kReadUncommitted //!< READ UNCOMMITTED @warning In Postgres READ UNCOMMITTED
32 //!< is treated as READ COMMITTED
33};
34/*! [Isolation levels] */
35
36std::string_view ToStringView(IsolationLevel lvl);
37
38/// @brief PostgreSQL transaction options
39///
40/// A transaction can be started using all isolation levels and modes
41/// supported by PostgreSQL server as specified in it's documentation.
42///
43/// Default isolation level is READ COMMITTED, default mode is READ WRITE.
44/// @code
45/// // Read-write read committed transaction.
46/// TransactionOptions opts;
47/// @endcode
48///
49/// Transaction class provides constants Transaction::RW, Transaction::RO and
50/// Transaction::Deferrable for convenience.
51///
52/// Other variants can be created with TransactionOptions constructors
53/// that are constexpr.
54///
55/// @see https://www.postgresql.org/docs/current/static/sql-set-transaction.html
57 /*! [Transaction modes] */
58 enum Mode : std::uint16_t {
59 kReadWrite = 0,
60 kReadOnly = 1,
61 kDeferrable = 3 //!< Deferrable transaction is read only
62 };
63 /*! [Transaction modes] */
65 Mode mode = kReadWrite;
66
67 constexpr TransactionOptions() = default;
68 constexpr explicit TransactionOptions(IsolationLevel lvl)
69 : isolation_level{lvl}
70 {}
71 constexpr TransactionOptions(IsolationLevel lvl, Mode m)
72 : isolation_level{lvl},
73 mode{m}
74 {}
75 constexpr explicit TransactionOptions(Mode m)
76 : mode{m}
77 {}
78
79 bool IsReadOnly() const { return mode & kReadOnly; }
80
81 /// The deferrable property has effect only if the transaction is also
82 /// serializable and read only
84};
85
86constexpr inline bool operator==(TransactionOptions lhs, TransactionOptions rhs) {
87 return lhs.isolation_level == rhs.isolation_level && lhs.mode == rhs.mode;
88}
89USERVER_NAMESPACE::utils::StringLiteral BeginStatement(TransactionOptions opts) noexcept;
90
91/// A structure to control timeouts for PosrgreSQL queries
92///
93/// There are two parameters, `execute` and `statement`.
94///
95/// `execute` parameter controls the overall time the driver spends executing a
96/// query, that includes:
97/// * connecting to PostgreSQL server, if there are no connections available and
98/// connection pool still has space for new connections;
99/// * waiting for a connection to become idle if there are no idle connections
100/// and connection pool already has reached it's max size;
101/// * preparing a statement if the statement is run for the first time on the
102/// connection;
103/// * binding parameters and executing the statement;
104/// * waiting for the first results to arrive from the server. If the result set
105/// is big, only time to the first data packet is taken into account.
106///
107/// `statement` is rather straightforward, it's the PostgreSQL server-side
108/// parameter, and it controls the time the database backend can spend executing
109/// a single statement. It is very costly to change the statement timeout
110/// often, as it requires a roundtrip to the database to change the setting.
111/// @see https://www.postgresql.org/docs/12/runtime-config-client.html
112///
113/// `execute` timeout should always be greater than the `statement` timeout!
114///
115/// In case of a timeout, either back-end or overall, the client gets an
116/// exception and the driver tries to clean up the connection for further reuse.
118 /// Overall timeout for a command being executed
119 TimeoutDuration network_timeout_ms{};
120 /// PostgreSQL server-side timeout
121 TimeoutDuration statement_timeout_ms{};
122
123 enum class PreparedStatementsOptionOverride { kNoOverride, kEnabled, kDisabled };
124
125 PreparedStatementsOptionOverride prepared_statements_enabled{PreparedStatementsOptionOverride::kNoOverride};
126
127 constexpr CommandControl(
128 TimeoutDuration network_timeout_ms,
129 TimeoutDuration statement_timeout_ms,
130 PreparedStatementsOptionOverride prepared_statements_enabled = PreparedStatementsOptionOverride::kNoOverride
131 )
132 : network_timeout_ms(network_timeout_ms),
133 statement_timeout_ms(statement_timeout_ms),
134 prepared_statements_enabled(prepared_statements_enabled)
135 {}
136
137 constexpr CommandControl WithExecuteTimeout(TimeoutDuration n) const noexcept { return {n, statement_timeout_ms}; }
138
139 constexpr CommandControl WithStatementTimeout(TimeoutDuration s) const noexcept { return {network_timeout_ms, s}; }
140
141 bool operator==(const CommandControl& rhs) const {
143 prepared_statements_enabled == rhs.prepared_statements_enabled;
144 }
145};
146
147/// @brief storages::postgres::CommandControl that may not be set
149
150using CommandControlByMethodMap = USERVER_NAMESPACE::utils::impl::TransparentMap<std::string, CommandControl>;
151using CommandControlByHandlerMap =
152 USERVER_NAMESPACE::utils::impl::TransparentMap<std::string, CommandControlByMethodMap>;
153using CommandControlByQueryMap = USERVER_NAMESPACE::utils::impl::TransparentMap<std::string, CommandControl>;
154
155OptionalCommandControl GetHandlerOptionalCommandControl(
156 const CommandControlByHandlerMap& map,
157 std::string_view path,
158 std::string_view method
159);
160
161OptionalCommandControl GetQueryOptionalCommandControl(const CommandControlByQueryMap& map, std::string_view query_name);
162
163/// Default initial pool connection count
164inline constexpr std::size_t kDefaultPoolMinSize = 4;
165
166/// Default maximum replication lag
167inline constexpr auto kDefaultMaxReplicationLag = std::chrono::seconds{60};
168
169/// Default pool connections limit
170inline constexpr std::size_t kDefaultPoolMaxSize = 15;
171
172/// Default size of queue for clients waiting for connections
173inline constexpr std::size_t kDefaultPoolMaxQueueSize = 200;
174
175/// Default limit for concurrent establishing connections number
176inline constexpr std::size_t kDefaultConnectingLimit = 0;
177
178/// Default minimum time between starting new connections per host in milliseconds
179inline constexpr std::size_t kDefaultConnectingIntervalMs = 0;
180
181/// Default connecting interval when pg-connecting-rate-limit experiment is enabled
182inline constexpr std::size_t kExperimentDefaultConnectingIntervalMs = 4000;
183
184/// @brief PostgreSQL topology options
185///
186/// Dynamic option @ref POSTGRES_TOPOLOGY_SETTINGS
188 /// Maximum replication lag. Once the replica lag exceeds this value it will be automatically disabled.
190
191 /// List of manually disabled replicas (FQDNs).
192 std::unordered_set<std::string, USERVER_NAMESPACE::utils::StrIcaseHash, USERVER_NAMESPACE::utils::StrIcaseEqual>
194};
195
196/// @brief PostgreSQL connection pool options
197///
198/// Dynamic option @ref POSTGRES_CONNECTION_POOL_SETTINGS
199struct PoolSettings final {
200 /// Number of connections created initially
202
203 /// Maximum number of created connections
205
206 /// Maximum number of clients waiting for a connection
208
209 /// Limits number of concurrent establishing connections (0 - unlimited)
211
212 /// Minimum time in milliseconds between starting new connections to each host (0 - unlimited)
214
215 bool operator==(const PoolSettings& rhs) const {
216 return min_size == rhs.min_size && max_size == rhs.max_size && max_queue_size == rhs.max_queue_size &&
218 }
219};
220
221// Configs with a suffix `Dynamic` are need to compatibility with static:
222// We must update only fields that were updated in a dynamic config (not a full config!).
223struct PoolSettingsDynamic final {
224 std::optional<std::size_t> min_size;
225 std::optional<std::size_t> max_size;
226 std::optional<std::size_t> max_queue_size;
227 std::optional<std::size_t> connecting_limit;
228 std::optional<std::size_t> connecting_interval_ms;
229};
230
231/// Minimal size for prepared statements cache. Matches the @ref POSTGRES_CONNECTION_SETTINGS minimum
232inline constexpr std::size_t kMinPreparedStatementsCacheSize = 3;
233
234/// Default size limit for prepared statements cache
235inline constexpr std::size_t kDefaultMaxPreparedCacheSize = 200;
236
237/// Pipeline mode configuration
238///
239/// Dynamic option @ref POSTGRES_CONNECTION_PIPELINE_EXPERIMENT
240enum class PipelineMode { kDisabled, kEnabled };
241
242/// Whether to omit excessive D(escribe) message
243/// when executing prepared statements
244///
245/// Dynamic option @ref POSTGRES_OMIT_DESCRIBE_IN_EXECUTE
246enum class OmitDescribeInExecuteMode { kDisabled, kEnabled };
247
248/// Connection pooler mode (e.g. Odyssey / PgBouncer)
249enum class PoolerMode { kSession, kTransaction };
250
251/// PostgreSQL connection options
252///
253/// Dynamic option @ref POSTGRES_CONNECTION_SETTINGS
255 enum PreparedStatementOptions {
256 kCachePreparedStatements,
257 kNoPreparedStatements,
258 };
259 enum UserTypesOptions {
260 kUserTypesEnabled,
261 kUserTypesEnforced,
262 kPredefinedTypesOnly,
263 };
264 enum CheckQueryParamsOptions {
265 kIgnoreUnused,
266 kCheckUnused,
267 };
268 enum DiscardOnConnectOptions {
269 kDiscardNone,
270 kDiscardAll,
271 };
272 enum StatementLogMode {
273 kLogSkip,
274 kLog,
275 };
276 using SettingsVersion = std::size_t;
277
278 /// Cache prepared statements or not
279 PreparedStatementOptions prepared_statements = kCachePreparedStatements;
280
281 /// Enables the usage of user-defined types
282 UserTypesOptions user_types = kUserTypesEnabled;
283
284 /// Checks for not-NULL query params that are not used in query
285 CheckQueryParamsOptions ignore_unused_query_params = kCheckUnused;
286
287 /// Limits the size or prepared statements cache
289
290 /// Turns on connection pipeline mode
292
293 /// Enables protocol-level optimization when executing prepared statements
295
296 /// This many connection errors in 15 seconds block new connections opening
297 std::size_t recent_errors_threshold = 5;
298
299 /// The maximum lifetime of the connection after which it will be closed
300 std::optional<std::chrono::seconds> max_ttl{};
301
302 /// Execute DISCARD ALL after establishing a new connection
303 /// Has effect only in session pooler mode (@ref PoolerMode::kSession)
304 DiscardOnConnectOptions discard_on_connect = kDiscardAll;
305
306 /// Statement logging in span tags
307 StatementLogMode statement_log_mode = kLog;
308
309 bool deadline_propagation_enabled = true;
310
311 /// Helps keep track of the changes in settings
312 SettingsVersion version{0U};
313
314 std::optional<std::string> application_name{};
315
316 PoolerMode pooler_mode{PoolerMode::kSession};
317
318 /// DANGEROUS. Has effect only in @ref PoolerMode::kTransaction. When set, no `SET statement_timeout` is sent
319 /// before autocommit statements, saving a round trip at the cost of losing statement_timeout enforcement (and
320 /// deadline propagation built on top of it) for such queries
322
323 bool operator==(const ConnectionSettings& rhs) const {
324 return !RequiresConnectionReset(rhs) && recent_errors_threshold == rhs.recent_errors_threshold;
325 }
326
327 bool RequiresConnectionReset(const ConnectionSettings& rhs) const {
328 // TODO: max_prepared_cache_size check could be relaxed
333 omit_describe_mode != rhs.omit_describe_mode || application_name != rhs.application_name ||
334 pooler_mode != rhs.pooler_mode ||
336 }
337};
338
339struct ConnectionSettingsDynamic final {
340 std::optional<ConnectionSettings::PreparedStatementOptions> prepared_statements{};
341 std::optional<ConnectionSettings::UserTypesOptions> user_types{};
342 std::optional<std::size_t> max_prepared_cache_size{};
343 std::optional<std::size_t> recent_errors_threshold{};
344 std::optional<ConnectionSettings::CheckQueryParamsOptions> ignore_unused_query_params{};
345 std::optional<std::chrono::seconds> max_ttl{};
346 std::optional<ConnectionSettings::DiscardOnConnectOptions> discard_on_connect{};
347 std::optional<bool> deadline_propagation_enabled{};
348 std::optional<bool> omit_statement_timeout_for_autocommit{};
349};
350
351/// @brief PostgreSQL statements metrics options
352///
353/// Dynamic option @ref POSTGRES_STATEMENT_METRICS_SETTINGS
354struct StatementMetricsSettings final {
355 /// Store metrics in LRU of this size
356 std::size_t max_statements{0};
357
358 bool operator==(const StatementMetricsSettings& other) const { return max_statements == other.max_statements; }
359};
360
361/// Initialization modes
362enum class InitMode {
363 kSync = 0,
364 kAsync,
365};
366
367enum class ConnlimitMode {
368 kManual = 0,
369 kAuto,
370};
371
372/// Settings for storages::postgres::Cluster
374 /// settings for statements metrics
375 StatementMetricsSettings statement_metrics_settings;
376
377 /// settings for host discovery
379
380 /// settings for connection pools
381 PoolSettings pool_settings;
382
383 /// settings for individual connections
385
386 /// initialization mode
388
389 /// database name
390 std::string db_name;
391
392 /// connection limit change mode
393 ConnlimitMode connlimit_mode = ConnlimitMode::kAuto;
394
395 /// congestion control settings
396 congestion_control::v2::LinearController::StaticConfig cc_config;
397};
398
399} // namespace storages::postgres
400
401USERVER_NAMESPACE_END