LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_db.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 34 58 58.6 %
Date: 2021-08-30 06:43:37 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2017 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_db.c
      18             :  * @brief Generic database operations for the exchange.
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <pthread.h>
      23             : #include <jansson.h>
      24             : #include <gnunet/gnunet_json_lib.h>
      25             : #include "taler_json_lib.h"
      26             : #include "taler_mhd_lib.h"
      27             : #include "taler-exchange-httpd_responses.h"
      28             : 
      29             : 
      30             : /**
      31             :  * How often should we retry a transaction before giving up
      32             :  * (for transactions resulting in serialization/dead locks only).
      33             :  *
      34             :  * The current value is likely too high for production. We might want to
      35             :  * benchmark good values once we have a good database setup.  The code is
      36             :  * expected to work correctly with any positive value, albeit inefficiently if
      37             :  * we too aggressively force clients to retry the HTTP request merely because
      38             :  * we have database serialization issues.
      39             :  */
      40             : #define MAX_TRANSACTION_COMMIT_RETRIES 100
      41             : 
      42             : 
      43             : /**
      44             :  * Ensure coin is known in the database, and handle conflicts and errors.
      45             :  *
      46             :  * @param coin the coin to make known
      47             :  * @param connection MHD request context
      48             :  * @param[out] mhd_ret set to MHD status on error
      49             :  * @return transaction status, negative on error (@a mhd_ret will be set in this case)
      50             :  */
      51             : enum GNUNET_DB_QueryStatus
      52          76 : TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
      53             :                      struct MHD_Connection *connection,
      54             :                      MHD_RESULT *mhd_ret)
      55             : {
      56             :   enum TALER_EXCHANGEDB_CoinKnownStatus cks;
      57             : 
      58             :   /* make sure coin is 'known' in database */
      59          76 :   cks = TEH_plugin->ensure_coin_known (TEH_plugin->cls,
      60             :                                        coin);
      61          76 :   switch (cks)
      62             :   {
      63          37 :   case TALER_EXCHANGEDB_CKS_ADDED:
      64          37 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
      65          36 :   case TALER_EXCHANGEDB_CKS_PRESENT:
      66          36 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
      67           0 :   case TALER_EXCHANGEDB_CKS_SOFT_FAIL:
      68           0 :     return GNUNET_DB_STATUS_SOFT_ERROR;
      69           0 :   case TALER_EXCHANGEDB_CKS_HARD_FAIL:
      70             :     *mhd_ret
      71           0 :       = TALER_MHD_reply_with_error (connection,
      72             :                                     MHD_HTTP_INTERNAL_SERVER_ERROR,
      73             :                                     TALER_EC_GENERIC_DB_STORE_FAILED,
      74             :                                     NULL);
      75           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
      76           3 :   case TALER_EXCHANGEDB_CKS_CONFLICT:
      77           3 :     break;
      78             :   }
      79             : 
      80           3 :   {
      81             :     struct TALER_EXCHANGEDB_TransactionList *tl;
      82             :     enum GNUNET_DB_QueryStatus qs;
      83             : 
      84           3 :     qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
      85             :                                             &coin->coin_pub,
      86             :                                             GNUNET_NO,
      87             :                                             &tl);
      88           3 :     if (0 > qs)
      89             :     {
      90           0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
      91           0 :         *mhd_ret = TALER_MHD_reply_with_error (
      92             :           connection,
      93             :           MHD_HTTP_INTERNAL_SERVER_ERROR,
      94             :           TALER_EC_GENERIC_DB_FETCH_FAILED,
      95             :           NULL);
      96           0 :       return qs;
      97             :     }
      98             :     *mhd_ret
      99           3 :       = TEH_RESPONSE_reply_coin_insufficient_funds (
     100             :           connection,
     101             :           TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
     102             :           &coin->coin_pub,
     103             :           tl);
     104           3 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     105             :                                             tl);
     106           3 :     return GNUNET_DB_STATUS_HARD_ERROR;
     107             :   }
     108             : }
     109             : 
     110             : 
     111             : /**
     112             :  * Run a database transaction for @a connection.
     113             :  * Starts a transaction and calls @a cb.  Upon success,
     114             :  * attempts to commit the transaction.  Upon soft failures,
     115             :  * retries @a cb a few times.  Upon hard or persistent soft
     116             :  * errors, generates an error message for @a connection.
     117             :  *
     118             :  * @param connection MHD connection to run @a cb for, can be NULL
     119             :  * @param name name of the transaction (for debugging)
     120             :  * @param[out] mhd_ret set to MHD response code, if transaction failed;
     121             :  *             NULL if we are not running with a @a connection and thus
     122             :  *             must not queue MHD replies
     123             :  * @param cb callback implementing transaction logic
     124             :  * @param cb_cls closure for @a cb, must be read-only!
     125             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
     126             :  */
     127             : enum GNUNET_GenericReturnValue
     128         240 : TEH_DB_run_transaction (struct MHD_Connection *connection,
     129             :                         const char *name,
     130             :                         MHD_RESULT *mhd_ret,
     131             :                         TEH_DB_TransactionCallback cb,
     132             :                         void *cb_cls)
     133             : {
     134         240 :   if (NULL != mhd_ret)
     135         240 :     *mhd_ret = -1; /* set to invalid value, to help detect bugs */
     136         240 :   if (GNUNET_OK !=
     137         240 :       TEH_plugin->preflight (TEH_plugin->cls))
     138             :   {
     139           0 :     GNUNET_break (0);
     140           0 :     if (NULL != mhd_ret)
     141           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     142             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     143             :                                              TALER_EC_GENERIC_DB_SETUP_FAILED,
     144             :                                              NULL);
     145           0 :     return GNUNET_SYSERR;
     146             :   }
     147         240 :   for (unsigned int retries = 0;
     148             :        retries < MAX_TRANSACTION_COMMIT_RETRIES;
     149           0 :        retries++)
     150             :   {
     151             :     enum GNUNET_DB_QueryStatus qs;
     152             : 
     153         240 :     if (GNUNET_OK !=
     154         240 :         TEH_plugin->start (TEH_plugin->cls,
     155             :                            name))
     156             :     {
     157           0 :       GNUNET_break (0);
     158           0 :       if (NULL != mhd_ret)
     159           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     160             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     161             :                                                TALER_EC_GENERIC_DB_START_FAILED,
     162             :                                                NULL);
     163           0 :       return GNUNET_SYSERR;
     164             :     }
     165         240 :     qs = cb (cb_cls,
     166             :              connection,
     167             :              mhd_ret);
     168         240 :     if (0 > qs)
     169          29 :       TEH_plugin->rollback (TEH_plugin->cls);
     170         240 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     171          29 :       return GNUNET_SYSERR;
     172         211 :     if (0 <= qs)
     173         211 :       qs = TEH_plugin->commit (TEH_plugin->cls);
     174         211 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     175             :     {
     176           0 :       if (NULL != mhd_ret)
     177           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     178             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     179             :                                                TALER_EC_GENERIC_DB_COMMIT_FAILED,
     180             :                                                NULL);
     181           0 :       return GNUNET_SYSERR;
     182             :     }
     183             :     /* make sure callback did not violate invariants! */
     184         211 :     GNUNET_assert ( (NULL == mhd_ret) ||
     185             :                     (-1 == *mhd_ret) );
     186         211 :     if (0 <= qs)
     187         211 :       return GNUNET_OK;
     188             :   }
     189           0 :   TALER_LOG_ERROR ("Transaction `%s' commit failed %u times\n",
     190             :                    name,
     191             :                    MAX_TRANSACTION_COMMIT_RETRIES);
     192           0 :   if (NULL != mhd_ret)
     193           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     194             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     195             :                                            TALER_EC_GENERIC_DB_SOFT_FAILURE,
     196             :                                            NULL);
     197           0 :   return GNUNET_SYSERR;
     198             : }
     199             : 
     200             : 
     201             : /* end of taler-exchange-httpd_db.c */

Generated by: LCOV version 1.14