userver: userver/storages/postgres/cluster.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
cluster.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/cluster.hpp
4/// @brief @copybrief storages::postgres::Cluster
5
6#include <memory>
7
8#include <userver/clients/dns/resolver_fwd.hpp>
9#include <userver/dynamic_config/source.hpp>
10#include <userver/engine/task/task_processor_fwd.hpp>
11#include <userver/engine/task/task_with_result.hpp>
12#include <userver/error_injection/settings_fwd.hpp>
13#include <userver/testsuite/postgres_control.hpp>
14#include <userver/testsuite/tasks.hpp>
15
16#include <userver/storages/postgres/cluster_types.hpp>
17#include <userver/storages/postgres/database.hpp>
18#include <userver/storages/postgres/detail/non_transaction.hpp>
19#include <userver/storages/postgres/notify.hpp>
20#include <userver/storages/postgres/options.hpp>
21#include <userver/storages/postgres/query.hpp>
22#include <userver/storages/postgres/query_queue.hpp>
23#include <userver/storages/postgres/statistics.hpp>
24#include <userver/storages/postgres/transaction.hpp>
25
26/// @page pg_topology uPg: Cluster topology discovery
27///
28/// @par Principles of PgaaS role determination
29/// - Every host except master is in recovery state from PostgreSQL's POV.
30/// This means the check 'select pg_is_in_recovery()' returns `false` for the
31/// master and `true` for every other host type.
32/// - Some hosts are in sync slave mode. This may be determined by executing
33/// 'show synchronous_standby_names' on the master.
34/// See
35/// https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-SYNCHRONOUS-STANDBY-NAMES
36/// for more information.
37///
38/// @par PgaaS sync slaves lag
39/// By default, PgaaS synchronous slaves are working with 'synchronous_commit'
40/// set to 'remote_apply'. Therefore, sync slave may be lagging behind the
41/// master and thus is not truly 'synchronous' from the reader's POV,
42/// but things may change with time.
43///
44/// @par Implementation
45/// Topology update runs every second.
46///
47/// Every host is assigned a connection with special ID (4100200300).
48/// Using this connection we check for host availability, writability
49/// (master detection) and perform RTT measurements.
50///
51/// After the initial check we know about master presence and RTT for each host.
52/// Master host is queried about synchronous replication status. We use this
53/// info to identify synchronous slaves and to detect "quorum commit" presence.
54///
55///
56/// ----------
57///
58/// @htmlonly <div class="bottom-nav"> @endhtmlonly
59/// ⇦ @ref pg_errors | @ref scripts/docs/en/userver/pg_connlimit_mode_auto.md ⇨
60/// @htmlonly </div> @endhtmlonly
61
62USERVER_NAMESPACE_BEGIN
63
64namespace components {
65class Postgres;
66} // namespace components
67
68namespace storages::postgres {
69
70namespace detail {
71
72class ClusterImpl;
73using ClusterImplPtr = std::unique_ptr<ClusterImpl>;
74
75} // namespace detail
76
77/// @ingroup userver_clients
78///
79/// @brief Interface for executing queries on a cluster of PostgreSQL servers
80///
81/// See @ref pg_user_row_types "Typed PostgreSQL results" for usage examples of
82/// the storages::postgres::ResultSet.
83///
84/// Usually retrieved from components::Postgres component.
85///
86/// @todo Add information about topology discovery
87class Cluster {
88 public:
89 /// Cluster constructor
90 /// @param dsns List of DSNs to connect to
91 /// @param resolver asynchronous DNS resolver
92 /// @param bg_task_processor task processor for blocking connection operations
93 /// @param cluster_settings struct with settings fields:
94 /// task_data_keys_settings - settings for per-handler command controls
95 /// topology_settings - settings for host discovery
96 /// pool_settings - settings for connection pools
97 /// conn_settings - settings for individual connections
98 /// @param default_cmd_ctls default command execution options
99 /// @param testsuite_pg_ctl command execution options customizer for testsuite
100 /// @param ei_settings error injection settings
101 /// @note When `max_connection_pool_size` is reached, and no idle connections
102 /// available, `PoolError` is thrown for every new connection
103 /// request
104 Cluster(DsnList dsns, clients::dns::Resolver* resolver,
105 engine::TaskProcessor& bg_task_processor,
106 const ClusterSettings& cluster_settings,
107 DefaultCommandControls&& default_cmd_ctls,
108 const testsuite::PostgresControl& testsuite_pg_ctl,
109 const error_injection::Settings& ei_settings,
110 testsuite::TestsuiteTasks& testsuite_tasks,
111 dynamic_config::Source config_source, int shard_number);
112 ~Cluster();
113
114 /// Get cluster statistics
115 ///
116 /// The statistics object is too big to fit on stack
118
119 /// @name Transaction start
120 /// @{
121
122 /// Start a transaction in any available connection depending on transaction
123 /// options.
124 ///
125 /// If the transaction is RW, will start transaction in a connection
126 /// to master. If the transaction is RO, will start trying connections
127 /// starting with slaves.
128 /// @throws ClusterUnavailable if no hosts are available
129 Transaction Begin(const TransactionOptions&, OptionalCommandControl = {});
130
131 /// Start a transaction in a connection with specified host selection rules.
132 ///
133 /// If the requested host role is not available, may fall back to another
134 /// host role, see ClusterHostType.
135 /// If the transaction is RW, only master connection can be used.
136 /// @throws ClusterUnavailable if no hosts are available
137 Transaction Begin(ClusterHostTypeFlags, const TransactionOptions&,
138 OptionalCommandControl = {});
139
140 /// Start a named transaction in any available connection depending on
141 /// transaction options.
142 ///
143 /// If the transaction is RW, will start transaction in a connection
144 /// to master. If the transaction is RO, will start trying connections
145 /// starting with slaves.
146 /// `name` is used to set command control in config at runtime.
147 /// @throws ClusterUnavailable if no hosts are available
148 Transaction Begin(std::string name, const TransactionOptions&);
149
150 /// Start a named transaction in a connection with specified host selection
151 /// rules.
152 ///
153 /// If the requested host role is not available, may fall back to another
154 /// host role, see ClusterHostType.
155 /// If the transaction is RW, only master connection can be used.
156 /// `name` is used to set command control in config at runtime.
157 /// @throws ClusterUnavailable if no hosts are available
158 Transaction Begin(std::string name, ClusterHostTypeFlags,
159 const TransactionOptions&);
160 /// @}
161
162 /// Start a query queue with specified host selection rules and timeout for
163 /// acquiring a connection.
164 [[nodiscard]] QueryQueue CreateQueryQueue(ClusterHostTypeFlags flags);
165
166 /// Start a query queue with specified host selection rules and timeout for
167 /// acquiring a connection.
168 [[nodiscard]] QueryQueue CreateQueryQueue(ClusterHostTypeFlags flags,
169 TimeoutDuration acquire_timeout);
170
171 /// @name Single-statement query in an auto-commit transaction
172 /// @{
173
174 /// @brief Execute a statement at host of specified type.
175 /// @note You must specify at least one role from ClusterHostType here
176 ///
177 /// @snippet storages/postgres/tests/landing_test.cpp Exec sample
178 ///
179 /// @warning Do NOT create a query string manually by embedding arguments!
180 /// It leads to vulnerabilities and bad performance. Either pass arguments
181 /// separately, or use storages::postgres::ParameterScope.
182 template <typename... Args>
183 ResultSet Execute(ClusterHostTypeFlags, const Query& query,
184 const Args&... args);
185
186 /// @brief Execute a statement with specified host selection rules and command
187 /// control settings.
188 /// @note You must specify at least one role from ClusterHostType here
189 ///
190 /// @warning Do NOT create a query string manually by embedding arguments!
191 /// It leads to vulnerabilities and bad performance. Either pass arguments
192 /// separately, or use storages::postgres::ParameterScope.
193 template <typename... Args>
194 ResultSet Execute(ClusterHostTypeFlags, OptionalCommandControl,
195 const Query& query, const Args&... args);
196
197 /// @brief Execute a statement with stored arguments and specified host
198 /// selection rules.
199 ResultSet Execute(ClusterHostTypeFlags flags, const Query& query,
200 const ParameterStore& store);
201
202 /// @brief Execute a statement with stored arguments, specified host selection
203 /// rules and command control settings.
204 ResultSet Execute(ClusterHostTypeFlags flags,
205 OptionalCommandControl statement_cmd_ctl,
206 const Query& query, const ParameterStore& store);
207 /// @}
208
209 /// @brief Listen for notifications on channel
210 /// @warning Each NotifyScope owns a single connection taken from the pool,
211 /// which effectively decreases the number of usable connections
212 NotifyScope Listen(std::string_view channel, OptionalCommandControl = {});
213
214 /// Replaces globally updated command control with a static user-provided one
216
217 /// Returns current default command control
219
220 void SetHandlersCommandControl(
221 CommandControlByHandlerMap handlers_command_control);
222
223 void SetQueriesCommandControl(
224 CommandControlByQueryMap queries_command_control);
225
226 /// @cond
227 /// Updates default command control from global config (if not set by user)
228 void ApplyGlobalCommandControlUpdate(CommandControl);
229 /// @endcond
230
231 /// Replaces cluster connection settings.
232 ///
233 /// Connections with an old settings will be dropped and reestablished.
235
236 void SetPoolSettings(const PoolSettings& settings);
237
238 void SetTopologySettings(const TopologySettings& settings);
239
240 void SetStatementMetricsSettings(const StatementMetricsSettings& settings);
241
242 private:
243 detail::NonTransaction Start(ClusterHostTypeFlags, OptionalCommandControl);
244
245 OptionalCommandControl GetQueryCmdCtl(const std::string& query_name) const;
246 OptionalCommandControl GetHandlersCmdCtl(
247 OptionalCommandControl cmd_ctl) const;
248
249 detail::ClusterImplPtr pimpl_;
250};
251
252template <typename... Args>
253ResultSet Cluster::Execute(ClusterHostTypeFlags flags, const Query& query,
254 const Args&... args) {
255 return Execute(flags, OptionalCommandControl{}, query, args...);
256}
257
258template <typename... Args>
259ResultSet Cluster::Execute(ClusterHostTypeFlags flags,
260 OptionalCommandControl statement_cmd_ctl,
261 const Query& query, const Args&... args) {
262 if (!statement_cmd_ctl && query.GetName()) {
263 statement_cmd_ctl = GetQueryCmdCtl(query.GetName()->GetUnderlying());
264 }
265 statement_cmd_ctl = GetHandlersCmdCtl(statement_cmd_ctl);
266 auto ntrx = Start(flags, statement_cmd_ctl);
267 return ntrx.Execute(statement_cmd_ctl, query, args...);
268}
269
270} // namespace storages::postgres
271
272USERVER_NAMESPACE_END