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
This commit is contained in:
parent
bb7c58921e
commit
e140ca9dce
@ -91,6 +91,27 @@ transaction_start_label: /* we will use goto for retries */ \
|
||||
} /* end of scope opened by BEGIN_TRANSACTION */
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user