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 */
|
} /* 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.
|
* Calculate the total value of all transactions performed.
|
||||||
* Stores @a off plus the cost of all transactions in @a tl
|
* 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 value;
|
||||||
struct TALER_Amount fee_withdraw;
|
struct TALER_Amount fee_withdraw;
|
||||||
int res;
|
int res;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Check if balance is sufficient */
|
/* Check if balance is sufficient */
|
||||||
START_TRANSACTION (session, connection);
|
START_TRANSACTION (session, connection);
|
||||||
@ -794,10 +816,10 @@ execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
|
|||||||
collectable.reserve_pub = *reserve;
|
collectable.reserve_pub = *reserve;
|
||||||
collectable.h_coin_envelope = *h_blind;
|
collectable.h_coin_envelope = *h_blind;
|
||||||
collectable.reserve_sig = *signature;
|
collectable.reserve_sig = *signature;
|
||||||
if (GNUNET_OK !=
|
ret = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
|
||||||
TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
|
session,
|
||||||
session,
|
&collectable);
|
||||||
&collectable))
|
if (GNUNET_SYSERR == ret)
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
TEH_plugin->rollback (TEH_plugin->cls,
|
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,
|
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||||
TALER_EC_WITHDRAW_DB_STORE_ERROR);
|
TALER_EC_WITHDRAW_DB_STORE_ERROR);
|
||||||
}
|
}
|
||||||
|
if (GNUNET_NO == ret)
|
||||||
|
RETRY_TRANSACTION(session, connection);
|
||||||
COMMIT_TRANSACTION (session, connection);
|
COMMIT_TRANSACTION (session, connection);
|
||||||
|
|
||||||
return TEH_RESPONSE_reply_reserve_withdraw_success (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)
|
* @param collectable corresponding collectable coin (blind signature)
|
||||||
* if a coin is found
|
* if a coin is found
|
||||||
* @return #GNUNET_SYSERR on internal error
|
* @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
|
* #GNUNET_YES on success
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
@ -2018,8 +2018,13 @@ postgres_insert_withdraw_info (void *cls,
|
|||||||
&reserve.balance,
|
&reserve.balance,
|
||||||
&collectable->amount_with_fee))
|
&collectable->amount_with_fee))
|
||||||
{
|
{
|
||||||
/* Should have been checked before we got here... */
|
/* The reserve history was checked to make sure there is enough of a balance
|
||||||
GNUNET_break (0); /* FIXME: this actually happens: #4794 */
|
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;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
expiry = GNUNET_TIME_absolute_add (now,
|
expiry = GNUNET_TIME_absolute_add (now,
|
||||||
|
Loading…
Reference in New Issue
Block a user