implement rh caching

This commit is contained in:
Christian Grothoff 2020-01-17 12:52:24 +01:00
parent 153dcdbc61
commit 540b22ce1c
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 185 additions and 78 deletions

View File

@ -66,6 +66,11 @@ struct ExchangeHttpRequestClosure
* Opaque parsing context. * Opaque parsing context.
*/ */
void *opaque_post_parsing_context; void *opaque_post_parsing_context;
/**
* Cached request handler for this request (once we have found one).
*/
struct TEH_RequestHandler *rh;
}; };
@ -360,7 +365,6 @@ handle_mhd_request (void *cls,
"<html><title>404: not found</title></html>", 0, "<html><title>404: not found</title></html>", 0,
&TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
}; };
struct TEH_RequestHandler *rh;
struct ExchangeHttpRequestClosure *ecls = *con_cls; struct ExchangeHttpRequestClosure *ecls = *con_cls;
int ret; int ret;
void **inner_cls; void **inner_cls;
@ -413,18 +417,28 @@ handle_mhd_request (void *cls,
"Handling request (%s) for URL '%s'\n", "Handling request (%s) for URL '%s'\n",
method, method,
url); url);
/* on repeated requests, check our cache first */
if (NULL != ecls->rh)
{
ret = ecls->rh->handler (ecls->rh,
connection,
inner_cls,
upload_data,
upload_data_size);
GNUNET_async_scope_restore (&old_scope);
return ret;
}
if (0 == strcasecmp (method, if (0 == strcasecmp (method,
MHD_HTTP_METHOD_HEAD)) MHD_HTTP_METHOD_HEAD))
method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
for (unsigned int i = 0; NULL != handlers[i].url; i++) for (unsigned int i = 0; NULL != handlers[i].url; i++)
{ {
rh = &handlers[i]; struct TEH_RequestHandler *rh = &handlers[i];
if (0 != strcmp (url, rh->url)) if (0 != strcmp (url, rh->url))
continue; continue;
/* The URL is a match! What we now do depends on the method. */ /* The URL is a match! What we now do depends on the method. */
if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS)) if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS))
{ {
GNUNET_async_scope_restore (&old_scope); GNUNET_async_scope_restore (&old_scope);
@ -435,8 +449,9 @@ handle_mhd_request (void *cls,
(0 == strcasecmp (method, (0 == strcasecmp (method,
rh->method)) ) rh->method)) )
{ {
/* FIXME: consider caching 'rh' in '**connection_cls' to /* cache to avoid the loop next time */
avoid repeated lookup! */ ecls->rh = rh;
/* run handler */
ret = rh->handler (rh, ret = rh->handler (rh,
connection, connection,
inner_cls, inner_cls,
@ -446,6 +461,7 @@ handle_mhd_request (void *cls,
return ret; return ret;
} }
} }
/* No handler matches, generate not found */
ret = TEH_MHD_handler_static_response (&h404, ret = TEH_MHD_handler_static_response (&h404,
connection, connection,
inner_cls, inner_cls,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2017 Inria and GNUnet e.V. Copyright (C) 2014-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -131,6 +131,24 @@ reply_refund_conflict (struct MHD_Connection *connection,
} }
/**
* Closure for the transaction.
*/
struct TALER_EXCHANGEDB_RefundContext
{
/**
* Information about the refund.
*/
const struct TALER_EXCHANGEDB_Refund *refund;
/**
* Expected refund fee by the denomination of the coin.
*/
struct TALER_Amount expect_fee;
};
/** /**
* Execute a "/refund" transaction. Returns a confirmation that the * Execute a "/refund" transaction. Returns a confirmation that the
* refund was successful, or a failure if we are not aware of a * refund was successful, or a failure if we are not aware of a
@ -155,20 +173,15 @@ refund_transaction (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret) int *mhd_ret)
{ {
const struct TALER_EXCHANGEDB_Refund *refund = cls; struct TALER_EXCHANGEDB_RefundContext *rc = cls;
const struct TALER_EXCHANGEDB_Refund *refund = rc->refund;
struct TALER_EXCHANGEDB_TransactionList *tl; struct TALER_EXCHANGEDB_TransactionList *tl;
const struct TALER_EXCHANGEDB_DepositListEntry *dep; const struct TALER_EXCHANGEDB_DepositListEntry *dep;
const struct TALER_EXCHANGEDB_RefundListEntry *ref; const struct TALER_EXCHANGEDB_RefundListEntry *ref;
struct TEH_KS_StateHandle *mks;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TALER_Amount expect_fee;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
int deposit_found; int deposit_found;
int refund_found; int refund_found;
int fee_cmp; int fee_cmp;
unsigned int hc;
enum TALER_ErrorCode ec;
struct TALER_CoinPublicInfo coin_info;
dep = NULL; dep = NULL;
ref = NULL; ref = NULL;
@ -356,64 +369,9 @@ refund_transaction (void *cls,
TALER_EC_REFUND_INSUFFICIENT_FUNDS); TALER_EC_REFUND_INSUFFICIENT_FUNDS);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
qs = TEH_plugin->get_known_coin (TEH_plugin->cls,
session,
&refund->coin.coin_pub,
&coin_info);
if (0 > qs)
{
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0); /* should be impossible by foreign key constraint! */
*mhd_ret = reply_refund_failure (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_REFUND_COIN_NOT_FOUND);
}
return qs;
}
GNUNET_CRYPTO_rsa_signature_free (coin_info.denom_sig.rsa_signature);
coin_info.denom_sig.rsa_signature = NULL; /* just to be safe */
// FIXME: do this outside of transaction function?
/* Check refund fee matches fee of denomination key! */ /* Check refund fee matches fee of denomination key! */
mks = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
if (NULL == mks)
{
TALER_LOG_ERROR ("Lacking keys to operate\n");
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
"no keys");
return GNUNET_DB_STATUS_HARD_ERROR;
}
dki = TEH_KS_denomination_key_lookup_by_hash (mks,
&coin_info.denom_pub_hash,
TEH_KS_DKU_DEPOSIT,
&ec,
&hc);
if (NULL == dki)
{
/* DKI not found, but we do have a coin with this DK in our database;
not good... */
GNUNET_break (0);
TEH_KS_release (mks);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
*mhd_ret = TALER_MHD_reply_with_error (connection,
hc,
ec,
"denomination not found, but coin known");
return GNUNET_DB_STATUS_HARD_ERROR;
}
TALER_amount_ntoh (&expect_fee,
&dki->issue.properties.fee_refund);
fee_cmp = TALER_amount_cmp (&refund->details.refund_fee, fee_cmp = TALER_amount_cmp (&refund->details.refund_fee,
&expect_fee); &rc->expect_fee);
TEH_KS_release (mks);
if (-1 == fee_cmp) if (-1 == fee_cmp)
{ {
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
@ -464,8 +422,9 @@ static int
verify_and_execute_refund (struct MHD_Connection *connection, verify_and_execute_refund (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Refund *refund) const struct TALER_EXCHANGEDB_Refund *refund)
{ {
struct TALER_EXCHANGEDB_RefundContext rc;
struct TALER_RefundRequestPS rr; struct TALER_RefundRequestPS rr;
int mhd_ret; struct GNUNET_HashCode denom_hash;
if (GNUNET_YES != if (GNUNET_YES !=
TALER_amount_cmp_currency (&refund->details.refund_amount, TALER_amount_cmp_currency (&refund->details.refund_amount,
@ -508,13 +467,79 @@ verify_and_execute_refund (struct MHD_Connection *connection,
TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID, TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
"merchant_sig"); "merchant_sig");
} }
if (GNUNET_OK !=
TEH_DB_run_transaction (connection, /* Fetch the coin's denomination (hash) */
"run refund", {
&mhd_ret, enum GNUNET_DB_QueryStatus qs;
&refund_transaction,
(void *) refund)) qs = TEH_plugin->get_coin_denomination (TEH_plugin->cls,
return mhd_ret; NULL,
&refund->coin.coin_pub,
&denom_hash);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return reply_refund_failure (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_REFUND_COIN_NOT_FOUND);
}
}
{
struct TEH_KS_StateHandle *mks;
mks = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
if (NULL == mks)
{
TALER_LOG_ERROR ("Lacking keys to operate\n");
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
"no keys");
}
/* Obtain information about the coin's denomination! */
{
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
unsigned int hc;
enum TALER_ErrorCode ec;
dki = TEH_KS_denomination_key_lookup_by_hash (mks,
&denom_hash,
TEH_KS_DKU_DEPOSIT,
&ec,
&hc);
if (NULL == dki)
{
/* DKI not found, but we do have a coin with this DK in our database;
not good... */
GNUNET_break (0);
TEH_KS_release (mks);
return TALER_MHD_reply_with_error (connection,
hc,
ec,
"denomination not found, but coin known");
}
TALER_amount_ntoh (&rc.expect_fee,
&dki->issue.properties.fee_refund);
}
TEH_KS_release (mks);
}
/* Finally run the actual transaction logic */
{
int mhd_ret;
rc.refund = refund;
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
"run refund",
&mhd_ret,
&refund_transaction,
&rc))
{
return mhd_ret;
}
}
return reply_refund_success (connection, return reply_refund_success (connection,
&refund->coin.coin_pub, &refund->coin.coin_pub,
&refund->details); &refund->details);

View File

@ -550,6 +550,15 @@ postgres_get_session (void *cls)
" WHERE coin_pub=$1" " WHERE coin_pub=$1"
" FOR UPDATE;", " FOR UPDATE;",
1), 1),
/* Used in #postgres_get_coin_denomination() to fetch
the denomination public key hash for
a coin known to the exchange. */
GNUNET_PQ_make_prepare ("get_coin_denomination",
"SELECT"
" denom_pub_hash"
" FROM known_coins"
" WHERE coin_pub=$1",
1),
/* Lock deposit table; NOTE: we may want to eventually shard the /* Lock deposit table; NOTE: we may want to eventually shard the
deposit table to avoid this lock being the main point of deposit table to avoid this lock being the main point of
contention limiting transaction performance. */ contention limiting transaction performance. */
@ -3007,6 +3016,45 @@ postgres_get_known_coin (void *cls,
} }
/**
* Retrieve the denomination of a known coin.
*
* @param cls the plugin closure
* @param session the database session handle
* @param coin_pub the public key of the coin to search for
* @param denom_hash[out] where to store the hash of the coins denomination
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
postgres_get_coin_denomination (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct
TALER_CoinSpendPublicKeyP *coin_pub,
struct GNUNET_HashCode *denom_hash)
{
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
denom_hash),
GNUNET_PQ_result_spec_end
};
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Getting coin denomination of coin %s\n",
TALER_B2S (coin_pub));
if (NULL == session)
session = postgres_get_session (pc);
return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_coin_denomination",
params,
rs);
}
/** /**
* Insert a coin we know of into the DB. The coin can then be * Insert a coin we know of into the DB. The coin can then be
* referenced by tables for deposits, refresh and refund * referenced by tables for deposits, refresh and refund
@ -7263,6 +7311,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->count_known_coins = &postgres_count_known_coins; plugin->count_known_coins = &postgres_count_known_coins;
plugin->ensure_coin_known = &postgres_ensure_coin_known; plugin->ensure_coin_known = &postgres_ensure_coin_known;
plugin->get_known_coin = &postgres_get_known_coin; plugin->get_known_coin = &postgres_get_known_coin;
plugin->get_coin_denomination = &postgres_get_coin_denomination;
plugin->have_deposit = &postgres_have_deposit; plugin->have_deposit = &postgres_have_deposit;
plugin->mark_deposit_tiny = &postgres_mark_deposit_tiny; plugin->mark_deposit_tiny = &postgres_mark_deposit_tiny;
plugin->test_deposit_done = &postgres_test_deposit_done; plugin->test_deposit_done = &postgres_test_deposit_done;

View File

@ -1884,6 +1884,23 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_CoinPublicInfo *coin_info); struct TALER_CoinPublicInfo *coin_info);
/**
* Retrieve the denomination of a known coin.
*
* @param cls the plugin closure
* @param session the database session handle
* @param coin_pub the public key of the coin to search for
* @param denom_hash[out] where to store the hash of the coins denomination
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
(*get_coin_denomination)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct GNUNET_HashCode *denom_hash);
/** /**
* Check if we have the specified deposit already in the database. * Check if we have the specified deposit already in the database.
* *