commit 611c9160b9b35a28c43972b561bc08a62c3da1fa
parent fd8b151a7e6689fa1105b034aa9527e1cf1c3e56
Author: Christian Grothoff <christian@grothoff.org>
Date: Thu, 6 Aug 2026 22:51:48 +0200
handle DB errors better
Diffstat:
4 files changed, 57 insertions(+), 10 deletions(-)
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
@@ -1517,12 +1517,13 @@ drain_kyc_alerts (void *cls)
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to lookup transient aggregates!\n");
TALER_EXCHANGEDB_rollback (pg);
- GNUNET_assert (NULL == task);
- task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
- NULL);
+ GNUNET_free (au);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
/* serializiability issue, try again */
diff --git a/src/exchange/taler-exchange-expire.c b/src/exchange/taler-exchange-expire.c
@@ -254,8 +254,30 @@ abort_shard (struct Shard *s)
"expire",
s->shard_start.abs_value_us,
s->shard_end.abs_value_us);
- if (0 >= qs)
+ switch (qs)
{
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* good case, handled below */
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* Somebody else already reclaimed the shard; not our problem, and
+ certainly not a reason to terminate. */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Shard to abort was already released\n");
+ GNUNET_free (s);
+ return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* This is reached from the soft-error path of run_shard(), i.e. exactly
+ when contention is highest. The shard lock times out on its own, so
+ shutting the daemon down here does more harm than good,
+ let's just continue. */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Failed to abort shard due to serialization failure; the "
+ "lock will time out on its own\n");
+ GNUNET_free (s);
+ return;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ /* Serious database issue, probably best to exit */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to abort shard (%d)!\n",
qs);
diff --git a/src/exchange/taler-exchange-httpd_delete-purses-PURSE_PUB.c b/src/exchange/taler-exchange-httpd_delete-purses-PURSE_PUB.c
@@ -35,6 +35,11 @@
#include "exchange-database/do_purse_delete.h"
#include "exchange-database/event_notify.h"
+/**
+ * How often do we retry a transaction that failed to serialize?
+ */
+#define MAX_RETRIES 3
+
enum MHD_Result
TEH_handler_purses_delete (
@@ -83,13 +88,31 @@ TEH_handler_purses_delete (
}
{
- enum GNUNET_DB_QueryStatus qs;
+ enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_SOFT_ERROR;
- qs = TALER_EXCHANGEDB_do_purse_delete (TEH_pg,
- &purse_pub,
- &purse_sig,
- &decided,
- &found);
+ /* Under SERIALIZABLE a 40001 is routine, so retry rather than answering
+ the client with an internal error; this file has no retry loop of its
+ own and used to lump SOFT_ERROR in with HARD_ERROR (and with
+ SUCCESS_NO_RESULTS) via "qs <= 0". */
+ for (unsigned int i = 0; i<MAX_RETRIES; i++)
+ {
+ qs = TALER_EXCHANGEDB_do_purse_delete (TEH_pg,
+ &purse_pub,
+ &purse_sig,
+ &decided,
+ &found);
+ if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+ break;
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ TALER_LOG_WARNING (
+ "Repeated serialization failure deleting purse\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_SOFT_FAILURE,
+ "purse delete");
+ }
if (qs <= 0)
{
TALER_LOG_WARNING (
diff --git a/src/exchange/taler-exchange-router.c b/src/exchange/taler-exchange-router.c
@@ -267,6 +267,7 @@ release_shard (struct Shard *s)
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
GNUNET_break (0);
+ global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: