From 2f0015b80385364ffbacca71504a96a46db6f0e0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 5 Jun 2019 21:26:27 +0200 Subject: [PATCH] add locking to avoid transaction retries --- src/exchange/taler-exchange-httpd_deposit.c | 4 +-- .../taler-exchange-httpd_reserve_withdraw.c | 14 ++++---- src/exchangedb/plugin_exchangedb_postgres.c | 34 ++++++++++++++++--- src/exchangedb/test_exchangedb.c | 7 ++++ 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 5a1bf4963..5320c9c75 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -215,8 +215,8 @@ deposit_transaction (void *cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, tl); qs = TEH_plugin->insert_deposit (TEH_plugin->cls, - session, - deposit); + session, + deposit); if (GNUNET_DB_STATUS_HARD_ERROR == qs) { TALER_LOG_WARNING ("Failed to store /deposit information in database\n"); diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c b/src/exchange/taler-exchange-httpd_reserve_withdraw.c index c988cde68..32d53980d 100644 --- a/src/exchange/taler-exchange-httpd_reserve_withdraw.c +++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c @@ -59,7 +59,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection, struct TALER_Amount balance; json_history = TEH_RESPONSE_compile_reserve_history (rh, - &balance); + &balance); if ((NULL == json_history) /* Address the case where the ptr is not null, but * it fails "internally" to dump as string (= corrupted). */ @@ -180,9 +180,9 @@ struct WithdrawContext */ static enum GNUNET_DB_QueryStatus withdraw_transaction (void *cls, - struct MHD_Connection *connection, - struct TALER_EXCHANGEDB_Session *session, - int *mhd_ret) + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + int *mhd_ret) { struct WithdrawContext *wc = cls; struct TALER_EXCHANGEDB_Reserve r; @@ -197,9 +197,9 @@ withdraw_transaction (void *cls, wc->collectable.sig.rsa_signature = NULL; #endif qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls, - session, - &wc->wsrd.h_coin_envelope, - &wc->collectable); + session, + &wc->wsrd.h_coin_envelope, + &wc->collectable); if (0 > qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 35b9aa57d..9788c67ab 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -739,6 +739,12 @@ postgres_prepare (PGconn *db_conn) " WHERE reserve_pub=$1" " FOR UPDATE;", 1), + /* Lock withdraw table; NOTE: we may want to eventually shard the + deposit table to avoid this lock being the main point of + contention limiting transaction performance. */ + GNUNET_PQ_make_prepare ("lock_withdraw", + "LOCK TABLE reserves_out;", + 0), /* Used in #postgres_insert_withdraw_info() to store the signature of a blinded coin with the blinded coin's details before returning it during /reserve/withdraw. We store @@ -1039,7 +1045,12 @@ postgres_prepare (PGconn *db_conn) " WHERE refund_serial_id>=$1" " ORDER BY refund_serial_id ASC;", 1), - + /* Lock deposit table; NOTE: we may want to eventually shard the + deposit table to avoid this lock being the main point of + contention limiting transaction performance. */ + GNUNET_PQ_make_prepare ("lock_deposit", + "LOCK TABLE deposits;", + 0), /* Store information about a /deposit the exchange is to execute. Used in #postgres_insert_deposit(). */ GNUNET_PQ_make_prepare ("insert_deposit", @@ -2380,6 +2391,9 @@ postgres_get_withdraw_info (void *cls, const struct GNUNET_HashCode *h_blind, struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable) { + struct GNUNET_PQ_QueryParam no_params[] = { + GNUNET_PQ_query_param_end + }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_blind), GNUNET_PQ_query_param_end @@ -2399,12 +2413,17 @@ postgres_get_withdraw_info (void *cls, &collectable->withdraw_fee), GNUNET_PQ_result_spec_end }; + enum GNUNET_DB_QueryStatus qs; + if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (session->conn, + "lock_withdraw", + no_params))) + return qs; collectable->h_coin_envelope = *h_blind; return GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "get_withdraw_info", - params, - rs); + "get_withdraw_info", + params, + rs); } @@ -2876,6 +2895,9 @@ postgres_have_deposit (void *cls, GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_QueryParam no_params[] = { + GNUNET_PQ_query_param_end + }; struct TALER_EXCHANGEDB_Deposit deposit2; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_amount ("amount_with_fee", @@ -2892,6 +2914,10 @@ postgres_have_deposit (void *cls, }; enum GNUNET_DB_QueryStatus qs; + if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (session->conn, + "lock_deposit", + no_params))) + return qs; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting deposits for coin %s\n", TALER_B2S (&deposit->coin.coin_pub)); diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index db40f3653..cab05f08c 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1995,6 +1995,10 @@ run (void *cls) result = 10; deposit2 = deposit; + FAILIF (GNUNET_OK != + plugin->start (plugin->cls, + session, + "test-2")); RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->have_deposit (plugin->cls, @@ -2010,6 +2014,9 @@ run (void *cls) GNUNET_YES)); FAILIF (GNUNET_OK != test_melting (session)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + plugin->commit (plugin->cls, + session)); /* test insert_refund! */