1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
#include <iostream>
#include "test_helpers.hxx"
using namespace PGSTD;
using namespace pqxx;
// Test program for libpqxx. Simulate "in-doubt" transaction failure.
namespace
{
// Transaction class that simulates connection failure during commit.
class basic_flakytransaction : public dbtransaction
{
public:
bool simulate_failure;
protected:
basic_flakytransaction(connection_base &C, const string &I) :
namedclass("flakytransaction"),
dbtransaction(C, I),
simulate_failure(false)
{}
private:
virtual void do_commit()
{
if (simulate_failure) conn().simulate_failure();
try
{
DirectExec("COMMIT");
}
catch (const exception &e)
{
if (conn().is_open())
{
PQXX_CHECK(!simulate_failure, "Connection did not simulate failure.");
cerr << "Unexpected exception (connection still open)" << endl;
throw;
}
process_notice(e.what() + string("\n"));
const string Msg =
(simulate_failure ?
"Simulating commit failure" :
"UNEXPECTED COMMIT FAILURE");
process_notice(Msg + "\n");
throw in_doubt_error(Msg);
}
}
};
template<isolation_level ISOLATIONLEVEL=read_committed>
class flakytransaction : public basic_flakytransaction
{
public:
typedef isolation_traits<ISOLATIONLEVEL> isolation_tag;
explicit flakytransaction(connection_base &C, const string &TName) :
namedclass(fullname("transaction",isolation_tag::name()), TName),
basic_flakytransaction(C, isolation_tag::name())
{ Begin(); }
explicit flakytransaction(connection_base &C) :
namedclass(fullname("transaction",isolation_tag::name())),
basic_flakytransaction(C, isolation_tag::name())
{ Begin(); }
virtual ~flakytransaction() throw () { End(); }
};
// A transactor build to fail, at least for the first m_failcount commits
class FlakyTransactor : public transactor<flakytransaction<> >
{
int m_failcount;
public:
explicit FlakyTransactor(int failcount=0) :
transactor<flakytransaction<> >("FlakyTransactor"),
m_failcount(failcount)
{}
void operator()(argument_type &T)
{
T.simulate_failure = (m_failcount > 0);
T.exec("SELECT count(*) FROM pg_tables");
}
void on_doubt() throw ()
{
try
{
if (m_failcount > 0)
{
--m_failcount;
pqxx::test::expected_exception("Transactor outcome in doubt.");
}
else
{
cerr << "Transactor outcome in doubt!" << endl;
}
}
catch (const exception &)
{
}
}
};
void test_094(transaction_base &orgT)
{
connection_base &C(orgT.conn());
orgT.abort();
// Run without simulating failure
C.perform(FlakyTransactor(0), 1);
// Simulate one failure. The transactor will succeed on a second attempt, but
// since this is an in-doubt error, the framework does not retry.
PQXX_CHECK_THROWS(
C.perform(FlakyTransactor(1), 2),
in_doubt_error,
"Simulated failure did not lead to in-doubt error.");
}
} // namespace
PQXX_REGISTER_TEST_T(test_094, nontransaction)
|