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) 2001-2018, 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 {
64 
66 
99 template<typename TRANSACTION_CALLBACK>
100 inline auto perform(const TRANSACTION_CALLBACK &callback, int attempts=3)
101  -> decltype(callback())
102 {
103  if (attempts <= 0)
104  throw std::invalid_argument(
105  "Zero or negative number of attempts passed to pqxx::perform().");
106 
107  for (; attempts > 0; --attempts)
108  {
109  try
110  {
111  return callback();
112  }
113  catch (const in_doubt_error &)
114  {
115  // Not sure whether transaction went through or not. The last thing in
116  // the world that we should do now is try again!
117  throw;
118  }
119  catch (const statement_completion_unknown &)
120  {
121  // Not sure whether our last statement succeeded. Don't risk running it
122  // again.
123  throw;
124  }
125  catch (const broken_connection &)
126  {
127  // Connection failed. Definitely worth retrying.
128  if (attempts <= 1) throw;
129  continue;
130  }
131  catch (const transaction_rollback &)
132  {
133  // Some error that may well be transient, such as serialization failure
134  // or deadlock. Worth retrying.
135  if (attempts <= 1) throw;
136  continue;
137  }
138  }
139  throw pqxx::internal_error("No outcome reached on perform().");
140 }
141 
143 
157 template<typename TRANSACTION=transaction<read_committed>> class transactor
158 {
159 public:
160  using argument_type = TRANSACTION;
161  explicit transactor(const std::string &TName="transactor") : //[t04]
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 (!Done);
270 }
271 } // namespace pqxx
273 #include "pqxx/compiler-internal-post.hxx"
274 #endif
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:161
void on_abort(const char[]) noexcept
Optional overridable function to be called if transaction is aborted.
Definition: transactor.hxx:193
void operator()(TRANSACTION &T)
Overridable transaction definition; insert your database code here.
"Help, I don&#39;t know whether transaction was committed successfully!"
Definition: except.hxx:157
TRANSACTION argument_type
Definition: transactor.hxx:160
We can&#39;t tell whether our last statement succeeded.
Definition: except.hxx:189
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:22
void on_commit()
Optional overridable function to be called after successful commit.
Definition: transactor.hxx:200
Definition: transactor.hxx:157
auto perform(const TRANSACTION_CALLBACK &callback, int attempts=3) -> decltype(callback())
Simple way to execute a transaction with automatic retry.
Definition: transactor.hxx:100
The backend saw itself forced to roll back the ongoing transaction.
Definition: except.hxx:165
Exception class for lost or failed backend connection.
Definition: except.hxx:118
Internal error in libpqxx library.
Definition: except.hxx:205
void perform(const TRANSACTOR &T, int Attempts)
Definition: transactor.hxx:225
std::string name() const
The transactor&#39;s name.
Definition: transactor.hxx:217