summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-11-17 15:53:16 +0100
committerChristian Grothoff <christian@grothoff.org>2016-11-17 15:53:16 +0100
commite140ca9dcef9bd86f9c9214872107693ef8c441c (patch)
tree170cd7378b25cdcc7e3452f0b6953c1f1551550d
parentbb7c58921eb56d464ed2b8a3a6fbbed9c82919d8 (diff)
downloadexchange-e140ca9dcef9bd86f9c9214872107693ef8c441c.tar.gz
exchange-e140ca9dcef9bd86f9c9214872107693ef8c441c.tar.bz2
exchange-e140ca9dcef9bd86f9c9214872107693ef8c441c.zip
handle more nicely the case that concurrent withdraws have changed history, causing us to see a different balance just before the commit; in this case, just retry the transaction; this should fix #4794
-rw-r--r--src/exchange/taler-exchange-httpd_db.c32
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c11
2 files changed, 36 insertions, 7 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index 848d28822..2c6e90656 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -92,6 +92,27 @@ transaction_start_label: /* we will use goto for retries */ \
/**
+ * Code to include to retry a transaction, must only be used in between
+ * #START_TRANSACTION and #COMMIT_TRANSACTION.
+ *
+ * @param session session handle
+ * @param connection connection handle
+ */
+#define RETRY_TRANSACTION(session,connection) \
+ do { \
+ TEH_plugin->rollback (TEH_plugin->cls, \
+ session); \
+ if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
+ goto transaction_start_label; \
+ TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
+ transaction_retries, \
+ __FUNCTION__); \
+ return TEH_RESPONSE_reply_commit_error (connection, \
+ TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
+ } while (0)
+
+
+/**
* Calculate the total value of all transactions performed.
* Stores @a off plus the cost of all transactions in @a tl
* in @a ret.
@@ -647,6 +668,7 @@ execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
struct TALER_Amount value;
struct TALER_Amount fee_withdraw;
int res;
+ int ret;
/* Check if balance is sufficient */
START_TRANSACTION (session, connection);
@@ -794,10 +816,10 @@ execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
collectable.reserve_pub = *reserve;
collectable.h_coin_envelope = *h_blind;
collectable.reserve_sig = *signature;
- if (GNUNET_OK !=
- TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
- session,
- &collectable))
+ ret = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
+ session,
+ &collectable);
+ if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls,
@@ -805,6 +827,8 @@ execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_DB_STORE_ERROR);
}
+ if (GNUNET_NO == ret)
+ RETRY_TRANSACTION(session, connection);
COMMIT_TRANSACTION (session, connection);
return TEH_RESPONSE_reply_reserve_withdraw_success (connection,
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 0a7f93f77..7ae8b5753 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1971,7 +1971,7 @@ postgres_get_withdraw_info (void *cls,
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
* @return #GNUNET_SYSERR on internal error
- * #GNUNET_NO if the collectable was not found
+ * #GNUNET_NO if we failed but should retry the transaction
* #GNUNET_YES on success
*/
static int
@@ -2018,8 +2018,13 @@ postgres_insert_withdraw_info (void *cls,
&reserve.balance,
&collectable->amount_with_fee))
{
- /* Should have been checked before we got here... */
- GNUNET_break (0); /* FIXME: this actually happens: #4794 */
+ /* The reserve history was checked to make sure there is enough of a balance
+ left before we tried this; however, concurrent operations may have changed
+ the situation by now. We should re-try the transaction. */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Withdrawal from reserve `%s' refused due to balance missmatch. Retrying.\n",
+ TALER_B2S (&collectable->reserve_pub));
+ ret = GNUNET_NO;
goto cleanup;
}
expiry = GNUNET_TIME_absolute_add (now,