commit b9899d4cafa8e3a16f235ecad2816f7c397386a1
parent 8fa1244b92d3f779b67809713fa991f56d19ce78
Author: Christian Grothoff <christian@grothoff.org>
Date: Thu, 6 Aug 2026 22:42:23 +0200
retry KYC rule evaluation on serialization failure
Diffstat:
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
@@ -807,6 +807,40 @@ trigger_wire_transfer (struct AggregationUnit *au)
}
+/**
+ * A step of #evaluate_rules() failed. If that was a serialization failure
+ * and we still have retries left, roll the transaction back and evaluate the
+ * rules again from the top; the KYC measure, the transient's requirement_row
+ * and the append-only deferral record are written in one transaction and
+ * nothing else ever rescans transients, so dropping them here loses them for
+ * good.
+ *
+ * @param[in,out] au aggregation we are working on
+ * @param qs status that made the caller stop
+ * @return true if a retry was scheduled and the caller must return at once
+ */
+static bool
+retry_rule_evaluation (struct AggregationUnit *au,
+ enum GNUNET_DB_QueryStatus qs)
+{
+ if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+ return false;
+ if (au->retries++ >= MAX_RETRIES)
+ return false;
+ TALER_EXCHANGEDB_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization failure evaluating KYC rules, trying again!\n");
+ au->ru = TALER_EXCHANGEDB_begin_rule_update (
+ pg,
+ &attribute_key,
+ &au->h_normalized_payto,
+ false, /* aggregation does not apply to wallets */
+ &evaluate_rules,
+ au);
+ return (NULL != au->ru);
+}
+
+
static void
evaluate_rules (
struct AggregationUnit *au,
@@ -869,6 +903,9 @@ evaluate_rules (
{
TALER_KYCLOGIC_rules_free (lrs);
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (retry_rule_evaluation (au,
+ qs))
+ return;
cleanup_and_next (au);
return;
}
@@ -906,6 +943,10 @@ evaluate_rules (
"Failed to persist KYC requirement `%s' in DB!\n",
TALER_KYCLOGIC_rule2s (requirement));
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TALER_KYCLOGIC_rules_free (lrs);
+ if (retry_rule_evaluation (au,
+ qs))
+ return;
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
global_ret = EXIT_FAILURE;
cleanup_and_next (au);
@@ -928,6 +969,9 @@ evaluate_rules (
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to persist updated transient in in DB!\n");
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (retry_rule_evaluation (au,
+ qs))
+ return;
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
global_ret = EXIT_FAILURE;
cleanup_and_next (au);
@@ -951,6 +995,9 @@ evaluate_rules (
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to persist deferral reason in DB!\n");
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (retry_rule_evaluation (au,
+ qs))
+ return;
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
global_ret = EXIT_FAILURE;
cleanup_and_next (au);
@@ -971,7 +1018,15 @@ evaluate_rules (
}
/* First commit, turns the rollback in cleanup into a NOP! */
- commit_or_warn ();
+ qs = commit_or_warn ();
+ if (0 > qs)
+ {
+ if (retry_rule_evaluation (au,
+ qs))
+ return;
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ global_ret = EXIT_FAILURE;
+ }
cleanup_and_next (au);
}