add post HTTP request check for hanging transactions

This commit is contained in:
Christian Grothoff 2019-04-18 14:38:24 +02:00
parent 5d3ae9655e
commit 32a3a0ffb0
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 67 additions and 63 deletions

View File

@ -146,6 +146,12 @@ handle_mhd_completion_callback (void *cls,
return; return;
TEH_PARSE_post_cleanup_callback (*con_cls); TEH_PARSE_post_cleanup_callback (*con_cls);
*con_cls = NULL; *con_cls = NULL;
/* check that we didn't leave any transactions hanging */
/* NOTE: In high-performance production, we might want to
remove this. */
TEH_plugin->preflight (TEH_plugin->cls,
TEH_plugin->get_session (TEH_plugin->cls));
} }

View File

@ -87,9 +87,9 @@ TEH_DB_know_coin_transaction (void *cls,
int int
TEH_DB_run_transaction (struct MHD_Connection *connection, TEH_DB_run_transaction (struct MHD_Connection *connection,
const char *name, const char *name,
int *mhd_ret, int *mhd_ret,
TEH_DB_TransactionCallback cb, TEH_DB_TransactionCallback cb,
void *cb_cls) void *cb_cls)
{ {
struct TALER_EXCHANGEDB_Session *session; struct TALER_EXCHANGEDB_Session *session;
@ -100,7 +100,7 @@ TEH_DB_run_transaction (struct MHD_Connection *connection,
GNUNET_break (0); GNUNET_break (0);
if (NULL != mhd_ret) if (NULL != mhd_ret)
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_SETUP_FAILED); TALER_EC_DB_SETUP_FAILED);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TEH_plugin->preflight (TEH_plugin->cls, TEH_plugin->preflight (TEH_plugin->cls,
@ -110,23 +110,23 @@ TEH_DB_run_transaction (struct MHD_Connection *connection,
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
if (GNUNET_OK != if (GNUNET_OK !=
TEH_plugin->start (TEH_plugin->cls, TEH_plugin->start (TEH_plugin->cls,
session, session,
name)) name))
{ {
GNUNET_break (0); GNUNET_break (0);
if (NULL != mhd_ret) if (NULL != mhd_ret)
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_START_FAILED); TALER_EC_DB_START_FAILED);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
qs = cb (cb_cls, qs = cb (cb_cls,
connection, connection,
session, session,
mhd_ret); mhd_ret);
if (0 > qs) if (0 > qs)
TEH_plugin->rollback (TEH_plugin->cls, TEH_plugin->rollback (TEH_plugin->cls,
session); session);
if (GNUNET_DB_STATUS_HARD_ERROR == qs) if (GNUNET_DB_STATUS_HARD_ERROR == qs)
return GNUNET_SYSERR; return GNUNET_SYSERR;
if (0 <= qs) if (0 <= qs)
@ -135,13 +135,13 @@ TEH_DB_run_transaction (struct MHD_Connection *connection,
if (GNUNET_DB_STATUS_HARD_ERROR == qs) if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{ {
if (NULL != mhd_ret) if (NULL != mhd_ret)
*mhd_ret = TEH_RESPONSE_reply_commit_error (connection, *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
TALER_EC_DB_COMMIT_FAILED_HARD); TALER_EC_DB_COMMIT_FAILED_HARD);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
/* make sure callback did not violate invariants! */ /* make sure callback did not violate invariants! */
GNUNET_assert ( (NULL == mhd_ret) || GNUNET_assert ( (NULL == mhd_ret) ||
(-1 == *mhd_ret) ); (-1 == *mhd_ret) );
if (0 <= qs) if (0 <= qs)
return GNUNET_OK; return GNUNET_OK;
} }

View File

@ -130,9 +130,9 @@ struct DepositContext
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
deposit_transaction (void *cls, deposit_transaction (void *cls,
struct MHD_Connection *connection, struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret) int *mhd_ret)
{ {
struct DepositContext *dc = cls; struct DepositContext *dc = cls;
const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit; const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
@ -141,8 +141,8 @@ deposit_transaction (void *cls,
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->have_deposit (TEH_plugin->cls, qs = TEH_plugin->have_deposit (TEH_plugin->cls,
session, session,
deposit, deposit,
GNUNET_YES /* check refund deadline */); GNUNET_YES /* check refund deadline */);
if (qs < 0) if (qs < 0)
{ {
@ -159,19 +159,19 @@ deposit_transaction (void *cls,
struct TALER_Amount amount_without_fee; struct TALER_Amount amount_without_fee;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"/deposit replay, accepting again!\n"); "/deposit replay, accepting again!\n");
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_subtract (&amount_without_fee, TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee, &deposit->amount_with_fee,
&deposit->deposit_fee)); &deposit->deposit_fee));
*mhd_ret = reply_deposit_success (connection, *mhd_ret = reply_deposit_success (connection,
&deposit->coin.coin_pub, &deposit->coin.coin_pub,
&deposit->h_wire, &deposit->h_wire,
&deposit->h_contract_terms, &deposit->h_contract_terms,
deposit->timestamp, deposit->timestamp,
deposit->refund_deadline, deposit->refund_deadline,
&deposit->merchant_pub, &deposit->merchant_pub,
&amount_without_fee); &amount_without_fee);
/* Treat as 'hard' DB error as we want to rollback and /* Treat as 'hard' DB error as we want to rollback and
never try again. */ never try again. */
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
@ -184,13 +184,13 @@ deposit_transaction (void *cls,
session, session,
&deposit->coin.coin_pub, &deposit->coin.coin_pub,
GNUNET_NO, GNUNET_NO,
&tl); &tl);
if (0 > qs) if (0 > qs)
return qs; return qs;
if (GNUNET_OK != if (GNUNET_OK !=
TEH_DB_calculate_transaction_list_totals (tl, TEH_DB_calculate_transaction_list_totals (tl,
&spent, &spent,
&spent)) &spent))
{ {
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl); tl);
@ -239,7 +239,7 @@ deposit_transaction (void *cls,
*/ */
static int static int
verify_and_execute_deposit (struct MHD_Connection *connection, verify_and_execute_deposit (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Deposit *deposit) const struct TALER_EXCHANGEDB_Deposit *deposit)
{ {
struct TALER_DepositRequestPS dr; struct TALER_DepositRequestPS dr;
int mhd_ret; int mhd_ret;
@ -269,7 +269,7 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
{ {
TALER_LOG_WARNING ("Invalid signature on /deposit request\n"); TALER_LOG_WARNING ("Invalid signature on /deposit request\n");
return TEH_RESPONSE_reply_signature_invalid (connection, return TEH_RESPONSE_reply_signature_invalid (connection,
TALER_EC_DEPOSIT_COIN_SIGNATURE_INVALID, TALER_EC_DEPOSIT_COIN_SIGNATURE_INVALID,
"coin_sig"); "coin_sig");
} }
@ -284,7 +284,7 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
} }
dki = TEH_KS_denomination_key_lookup (mks, dki = TEH_KS_denomination_key_lookup (mks,
&deposit->coin.denom_pub, &deposit->coin.denom_pub,
TEH_KS_DKU_DEPOSIT); TEH_KS_DKU_DEPOSIT);
if (NULL == dki) if (NULL == dki)
{ {
TEH_KS_release (mks); TEH_KS_release (mks);
@ -300,9 +300,9 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
if (GNUNET_OK != if (GNUNET_OK !=
TEH_DB_run_transaction (connection, TEH_DB_run_transaction (connection,
"execute deposit", "execute deposit",
&mhd_ret, &mhd_ret,
&deposit_transaction, &deposit_transaction,
&dc)) &dc))
return mhd_ret; return mhd_ret;
/* generate regular response */ /* generate regular response */
@ -311,13 +311,13 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
&deposit->amount_with_fee, &deposit->amount_with_fee,
&deposit->deposit_fee)); &deposit->deposit_fee));
return reply_deposit_success (connection, return reply_deposit_success (connection,
&deposit->coin.coin_pub, &deposit->coin.coin_pub,
&deposit->h_wire, &deposit->h_wire,
&deposit->h_contract_terms, &deposit->h_contract_terms,
deposit->timestamp, deposit->timestamp,
deposit->refund_deadline, deposit->refund_deadline,
&deposit->merchant_pub, &deposit->merchant_pub,
&amount_without_fee); &amount_without_fee);
} }

View File

@ -1075,11 +1075,9 @@ postgres_prepare (PGconn *db_conn)
",h_contract_terms" ",h_contract_terms"
",h_wire" ",h_wire"
" FROM deposits" " FROM deposits"
" WHERE (" " WHERE ((coin_pub=$1)"
" (coin_pub=$1)"
" AND (merchant_pub=$3)" " AND (merchant_pub=$3)"
" AND (h_contract_terms=$2)" " AND (h_contract_terms=$2))"
" )"
" FOR UPDATE;", " FOR UPDATE;",
3), 3),
/* Fetch deposits with rowid '\geq' the given parameter */ /* Fetch deposits with rowid '\geq' the given parameter */
@ -2893,15 +2891,15 @@ postgres_have_deposit (void *cls,
struct TALER_EXCHANGEDB_Deposit deposit2; struct TALER_EXCHANGEDB_Deposit deposit2;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("amount_with_fee", TALER_PQ_result_spec_amount ("amount_with_fee",
&deposit2.amount_with_fee), &deposit2.amount_with_fee),
TALER_PQ_result_spec_absolute_time ("timestamp", TALER_PQ_result_spec_absolute_time ("timestamp",
&deposit2.timestamp), &deposit2.timestamp),
TALER_PQ_result_spec_absolute_time ("refund_deadline", TALER_PQ_result_spec_absolute_time ("refund_deadline",
&deposit2.refund_deadline), &deposit2.refund_deadline),
TALER_PQ_result_spec_absolute_time ("wire_deadline", TALER_PQ_result_spec_absolute_time ("wire_deadline",
&deposit2.wire_deadline), &deposit2.wire_deadline),
GNUNET_PQ_result_spec_auto_from_type ("h_wire", GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&deposit2.h_wire), &deposit2.h_wire),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
@ -2910,9 +2908,9 @@ postgres_have_deposit (void *cls,
"Getting deposits for coin %s\n", "Getting deposits for coin %s\n",
TALER_B2S (&deposit->coin.coin_pub)); TALER_B2S (&deposit->coin.coin_pub));
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_deposit", "get_deposit",
params, params,
rs); rs);
if (0 >= qs) if (0 >= qs)
return qs; return qs;
/* Now we check that the other information in @a deposit /* Now we check that the other information in @a deposit
@ -3650,19 +3648,19 @@ postgres_get_melt (void *cls,
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
&refresh_melt->session.coin.denom_pub.rsa_public_key), &refresh_melt->session.coin.denom_pub.rsa_public_key),
TALER_PQ_result_spec_amount ("fee_refresh", TALER_PQ_result_spec_amount ("fee_refresh",
&refresh_melt->melt_fee), &refresh_melt->melt_fee),
GNUNET_PQ_result_spec_rsa_signature ("denom_sig", GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
&refresh_melt->session.coin.denom_sig.rsa_signature), &refresh_melt->session.coin.denom_sig.rsa_signature),
GNUNET_PQ_result_spec_uint32 ("noreveal_index", GNUNET_PQ_result_spec_uint32 ("noreveal_index",
&refresh_melt->session.noreveal_index), &refresh_melt->session.noreveal_index),
GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub", GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
&refresh_melt->session.coin.coin_pub), &refresh_melt->session.coin.coin_pub),
GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
&refresh_melt->session.coin_sig), &refresh_melt->session.coin_sig),
TALER_PQ_result_spec_amount ("amount_with_fee", TALER_PQ_result_spec_amount ("amount_with_fee",
&refresh_melt->session.amount_with_fee), &refresh_melt->session.amount_with_fee),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;