libpqxx
transactor.hxx
1 /* Transactor framework, a wrapper for safely retryable transactions.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transactor instead.
4  *
5  * Copyright (c) 2000-2019, Jeroen T. Vermeulen.
6  *
7  * See COPYING for copyright license. If you did not receive a file called
8  * COPYING with this source code, please notify the distributor of this mistake,
9  * or contact the author.
10  */
11 #ifndef PQXX_H_TRANSACTOR
12 #define PQXX_H_TRANSACTOR
13 
14 #include "pqxx/compiler-public.hxx"
15 #include "pqxx/compiler-internal-pre.hxx"
16 
17 #include "pqxx/connection_base.hxx"
18 #include "pqxx/transaction.hxx"
19 
20 
21 // Methods tested in eg. test module test01 are marked with "//[t01]".
22 
23 namespace pqxx
24 {
67 
69 
98 template<typename TRANSACTION_CALLBACK>
99 inline auto perform(const TRANSACTION_CALLBACK &callback, int attempts=3)
100  -> decltype(callback())
101 {
102  if (attempts <= 0)
103  throw std::invalid_argument{
104  "Zero or negative number of attempts passed to pqxx::perform()."};
105 
106  for (; attempts > 0; --attempts)
107  {
108  try
109  {
110  return callback();
111  }
112  catch (const in_doubt_error &)
113  {
114  // Not sure whether transaction went through or not. The last thing in
115  // the world that we should do now is try again!
116  throw;
117  }
118  catch (const statement_completion_unknown &)
119  {
120  // Not sure whether our last statement succeeded. Don't risk running it
121  // again.
122  throw;
123  }
124  catch (const broken_connection &)
125  {
126  // Connection failed. Definitely worth retrying.
127  if (attempts <= 1) throw;
128  continue;
129  }
130  catch (const transaction_rollback &)
131  {
132  // Some error that may well be transient, such as serialization failure
133  // or deadlock. Worth retrying.
134  if (attempts <= 1) throw;
135  continue;
136  }
137  }
138  throw pqxx::internal_error{"No outcome reached on perform()."};
139 }
140 
142 
156 template<typename TRANSACTION=transaction<read_committed>> class transactor
157 {
158 public:
159  using argument_type = TRANSACTION;
160  PQXX_DEPRECATED explicit transactor( //[t04]
161  const std::string &TName="transactor") :
162  m_name{TName} { }
163 
165 
176  void operator()(TRANSACTION &T); //[t04]
177 
178  // Overridable member functions, called by connection_base::perform() if an
179  // attempt to run transaction fails/succeeds, respectively, or if the
180  // connection is lost at just the wrong moment, goes into an indeterminate
181  // state. Use these to patch up runtime state to match events, if needed, or
182  // to report failure conditions.
183 
185 
193  void on_abort(const char[]) noexcept {} //[t13]
194 
196 
200  void on_commit() {} //[t07]
201 
203 
214  void on_doubt() noexcept {} //[t13]
215 
217  std::string name() const { return m_name; } //[t13]
218 
219 private:
220  std::string m_name;
221 };
222 
223 
224 template<typename TRANSACTOR>
226  const TRANSACTOR &T,
227  int Attempts)
228 {
229  if (Attempts <= 0) return;
230 
231  bool Done = false;
232 
233  // Make attempts to perform T
234  do
235  {
236  --Attempts;
237 
238  // Work on a copy of T2 so we can restore the starting situation if need be
239  TRANSACTOR T2{T};
240  try
241  {
242  typename TRANSACTOR::argument_type X{*this, T2.name()};
243  T2(X);
244  X.commit();
245  Done = true;
246  }
247  catch (const in_doubt_error &)
248  {
249  // Not sure whether transaction went through or not. The last thing in
250  // the world that we should do now is retry.
251  T2.on_doubt();
252  throw;
253  }
254  catch (const std::exception &e)
255  {
256  // Could be any kind of error.
257  T2.on_abort(e.what());
258  if (Attempts <= 0) throw;
259  continue;
260  }
261  catch (...)
262  {
263  // Don't try to forge ahead if we don't even know what happened
264  T2.on_abort("Unknown exception");
265  throw;
266  }
267 
268  T2.on_commit();
269  } while (not Done);
270 }
271 } // namespace pqxx
273 #include "pqxx/compiler-internal-post.hxx"
274 #endif
void operator()(TRANSACTION &T)
Overridable transaction definition; insert your database code here.
The backend saw itself forced to roll back the ongoing transaction.
Definition: except.hxx:167
Exception class for lost or failed backend connection.
Definition: except.hxx:118
We can&#39;t tell whether our last statement succeeded.
Definition: except.hxx:191
"Help, I don&#39;t know whether transaction was committed successfully!"
Definition: except.hxx:159
void on_abort(const char[]) noexcept
Optional overridable function to be called if transaction is aborted.
Definition: transactor.hxx:193
std::string name() const
The transactor&#39;s name.
Definition: transactor.hxx:217
void on_commit()
Optional overridable function to be called after successful commit.
Definition: transactor.hxx:200
Internal error in libpqxx library.
Definition: except.hxx:207
Definition: transactor.hxx:156
auto perform(const TRANSACTION_CALLBACK &callback, int attempts=3) -> decltype(callback())
Simple way to execute a transaction with automatic retry.
Definition: transactor.hxx:99
TRANSACTION argument_type
Definition: transactor.hxx:159
void on_doubt() noexcept
Overridable function to be called when "in doubt" about outcome.
Definition: transactor.hxx:214
transactor(const std::string &TName="transactor")
Definition: transactor.hxx:160
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:25
void perform(const TRANSACTOR &T, int Attempts)
Definition: transactor.hxx:225