|
libpqxx
The C++ client library for PostgreSQL
|
Sometimes a transaction can fail for completely transient reasons, such as a conflict with another transaction in SERIALIZABLE isolation. The right way to handle those failures is often just to re-run the transaction from scratch.
For example, your REST API might be handling each HTTP request in its own database transaction, and if this kind of transient failure happens, you simply want to "replay" the whole request, in a fresh transaction.
You won't necessarily want to execute the exact same SQL commands with the exact same data. Some of your SQL statements may depend on state that may have changed by the time you replay the request. For example, your code may query information from the database that can change between retries. So instead of dumbly replaying the SQL, you re-run the same application code that produced those SQL commands, from the start.
The transactor framework makes it a little easier for you to do this safely, and avoid typical pitfalls. You encapsulate the work that you want to do into a callable that you pass to the perform function.
Here's how it works. You write your transaction code as a lambda or function, which creates its own transaction object, does its work, and commits at the end. You pass that callback to pqxx::perform, which runs it for you.
If there's a database-related failure inside your callback, libpqxx will throw an exception. Your transaction object goes out of scope and gets destroyed, at which point it aborts implicitly. Seeing this, perform tries running your callback again. It may have to repeat this a few times.
It stops trying that when...
That last scenario can happen for example when you lose your network connection to the database just while you're waiting for it to confirm the result of a commit. In that situation, there's no way for the client to know whether the transaction succeeded from the server's perspective (and so was committed), or whether it failed and got aborted. (If this possibility is a major concern to you, also have a look at the robusttransaction class. It does not help make your transaction more likely to succeed, but it tries harder to avoid these unknown states.)
The callback you pass to perform takes no arguments. If you're using lambdas, the easy way to pass arguments is for the lambda to "capture" them from your variables.
Once your callback succeeds, it can return a result of your choosing, and perform will return that result back to you.