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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
/** Definition of the pqxx::robusttransaction class.
*
* pqxx::robusttransaction is a slower but safer transaction class.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/robusttransaction instead.
*
* Copyright (c) 2000-2019, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this mistake,
* or contact the author.
*/
#ifndef PQXX_H_ROBUSTTRANSACTION
#define PQXX_H_ROBUSTTRANSACTION
#include "pqxx/compiler-public.hxx"
#include "pqxx/compiler-internal-pre.hxx"
#include "pqxx/dbtransaction.hxx"
// Methods tested in eg. test module test01 are marked with "//[t01]".
namespace pqxx
{
namespace internal
{
/// Helper base class for the @c robusttransaction class template.
class PQXX_LIBEXPORT PQXX_NOVTABLE basic_robusttransaction :
public dbtransaction
{
public:
/// Isolation level is read_committed by default.
using isolation_tag = isolation_traits<read_committed>;
virtual ~basic_robusttransaction() =0; //[t16]
protected:
basic_robusttransaction(
connection_base &C,
const std::string &IsolationLevel,
const std::string &table_name=std::string{}); //[t16]
private:
using IDType = unsigned long;
IDType m_record_id = 0;
std::string m_xid;
std::string m_log_table;
std::string m_sequence;
int m_backendpid = -1;
virtual void do_begin() override; //[t18]
virtual void do_commit() override; //[t16]
virtual void do_abort() override; //[t18]
PQXX_PRIVATE void CreateLogTable();
PQXX_PRIVATE void CreateTransactionRecord();
PQXX_PRIVATE std::string sql_delete() const;
PQXX_PRIVATE void DeleteTransactionRecord() noexcept;
PQXX_PRIVATE bool CheckTransactionRecord();
};
} // namespace internal
/**
* @ingroup transaction
*
* @{
*/
/// Slightly slower, better-fortified version of transaction
/** robusttransaction is similar to transaction, but spends more effort (and
* performance!) to deal with the hopefully rare case that the connection to
* the backend is lost just as the current transaction is being committed. In
* this case, there is no way to determine whether the backend managed to
* commit the transaction before noticing the loss of connection.
*
* In such cases, this class tries to reconnect to the database and figure out
* what happened. It will need to store and manage some information (pretty
* much a user-level transaction log) in the back-end for each and every
* transaction just on the off chance that this problem might occur.
* This service level was made optional since you may not want to pay this
* overhead where it is not necessary. Certainly the use of this class makes
* no sense for local connections, or for transactions that read the database
* but never modify it, or for noncritical database manipulations.
*
* Besides being slower, it's theoretically possible that robusttransaction
* actually fails more instead of less often than a normal transaction. This is
* due to the added work and complexity. What robusttransaction tries to
* achieve is to be more deterministic, not more successful per se.
*
* When a user first uses a robusttransaction in a database, the class will
* attempt to create a log table there to keep vital transaction-related state
* information in. This table, located in that same database, will be called
* pqxxlog_*user*, where *user* is the PostgreSQL username for that user. If
* the log table can not be created, the transaction fails immediately.
*
* If the user does not have permission to create the log table, the database
* administrator may create one for him beforehand, and give ownership (or at
* least full insert/update rights) to the user. The table must contain two
* non-unique fields (which will never be null): "name" (of text type,
* @c varchar(256) by default) and "date" (of @c timestamp type). Older
* versions of robusttransaction also added a unique "id" field; this field is
* now obsolete and the log table's implicit oids are used instead. The log
* tables' names may be made configurable in a future version of libpqxx.
*
* The transaction log table contains records describing unfinished
* transactions, i.e. ones that have been started but not, as far as the client
* knows, committed or aborted. This can mean any of the following:
*
* <ol>
* <li> The transaction is in progress. Since backend transactions can't run
* for extended periods of time, this can only be the case if the log record's
* timestamp (compared to the server's clock) is not very old, provided of
* course that the server's system clock hasn't just made a radical jump.
* <li> The client's connection to the server was lost, just when the client was
* committing the transaction, and the client so far has not been able to
* re-establish the connection to verify whether the transaction was actually
* completed or rolled back by the server. This is a serious (and luckily a
* rare) condition and requires manual inspection of the database to determine
* what happened. The robusttransaction will emit clear and specific warnings
* to this effect, and will identify the log record describing the transaction
* in question.
* <li> The transaction was completed (either by commit or by rollback), but the
* client's connection was durably lost just as it tried to clean up the log
* record. Again, robusttransaction will emit a clear and specific warning to
* tell you about this and request that the record be deleted as soon as
* possible.
* <li> The client has gone offline at any time while in one of the preceding
* states. This also requires manual intervention, but the client obviously is
* not able to issue a warning.
* </ol>
*
* It is safe to drop a log table when it is not in use (ie., it is empty or all
* records in it represent states 2-4 above). Each robusttransaction will
* attempt to recreate the table at its next time of use.
*/
template<isolation_level ISOLATIONLEVEL=read_committed>
class robusttransaction : public internal::basic_robusttransaction
{
public:
using isolation_tag = isolation_traits<ISOLATIONLEVEL>;
/// Constructor
/** Creates robusttransaction of given name
* @param C Connection that this robusttransaction should live inside.
* @param Name optional human-readable name for this transaction
*/
explicit robusttransaction(
connection_base &C,
const std::string &Name=std::string{}) :
namedclass{fullname("robusttransaction",isolation_tag::name()), Name},
internal::basic_robusttransaction(C, isolation_tag::name())
{ Begin(); }
virtual ~robusttransaction() noexcept
{ End(); }
};
/**
* @}
*/
} // namespace pqxx
#include "pqxx/compiler-internal-post.hxx"
#endif
|