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