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/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