commit 46da69710427458d402a55ce617ba28286923dba
parent fd8519d7cb869a5574cec6e686ef9c56505a73d4
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Sat, 15 Aug 2026 21:15:36 +0200
properly handle DB status on get_serial_by_table
Diffstat:
1 file changed, 64 insertions(+), 9 deletions(-)
diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c
@@ -580,6 +580,57 @@ do_insert (struct InsertContext *ctx,
/**
+ * Determine the last serial number of each table in @a db.
+ *
+ * Failing to determine a serial must abort the pass: the previous
+ * value of the field is stale (#tables is file-scope), and continuing
+ * with it would silently skip records or entire tables.
+ *
+ * @param db database to query
+ * @param source true to store the result in the `end_serial` of each
+ * table (data source), false for `start_serial` (destination)
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR to rollback
+ */
+static enum GNUNET_GenericReturnValue
+lookup_serials (struct TALER_EXCHANGEDB_PostgresContext *db,
+ bool source)
+{
+ for (unsigned int i = 0; ! tables[i].end; i++)
+ {
+ uint64_t *serial = source
+ ? &tables[i].end_serial
+ : &tables[i].start_serial;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TALER_EXCHANGEDB_get_serial_by_table (db,
+ tables[i].rt,
+ serial);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to determine last serial of table `%s': hard error\n",
+ rt_name (tables[i].rt));
+ global_ret = EXIT_FAILURE;
+ return GNUNET_SYSERR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Serialization error determining last serial of table `%s' (will retry)\n",
+ rt_name (tables[i].rt));
+ return GNUNET_SYSERR;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* Table is empty; the statement did not write *serial. */
+ *serial = 0;
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/**
* Run one replication transaction.
*
* @return #GNUNET_OK on success, #GNUNET_SYSERR to rollback
@@ -595,19 +646,19 @@ transact (void)
TALER_EXCHANGEDB_start (src,
"lookup src serials"))
return GNUNET_SYSERR;
- for (unsigned int i = 0; ! tables[i].end; i++)
- TALER_EXCHANGEDB_get_serial_by_table (src,
- tables[i].rt,
- &tables[i].end_serial);
+ if (GNUNET_OK !=
+ lookup_serials (src,
+ true))
+ return GNUNET_SYSERR;
TALER_EXCHANGEDB_rollback (src);
if (GNUNET_OK !=
TALER_EXCHANGEDB_start (dst,
"lookup dst serials"))
return GNUNET_SYSERR;
- for (unsigned int i = 0; ! tables[i].end; i++)
- TALER_EXCHANGEDB_get_serial_by_table (dst,
- tables[i].rt,
- &tables[i].start_serial);
+ if (GNUNET_OK !=
+ lookup_serials (dst,
+ false))
+ return GNUNET_SYSERR;
TALER_EXCHANGEDB_rollback (dst);
for (unsigned int i = 0; ! tables[i].end; i++)
{
@@ -673,7 +724,11 @@ transact (void)
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Serialization error committing transaction on table `%s' (will retry)\n",
rt_name (table->rt));
- continue;
+ /* The rows of the failed commit are gone, but start_serial was
+ already advanced past them by do_insert(); continuing here
+ would leave a permanent hole. Restart the pass instead, which
+ re-reads the serials actually present in the destination. */
+ return GNUNET_SYSERR;
}
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{