commit ea203ef668ce4be4e92048b4b4ab8c8d2ff721bc
parent 922097e0b6f94adece8fa526c196b5333843868d
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 13 Jun 2026 20:58:40 +0200
refactor to use new more robust GNUNET_PQ_init() style to handle database connection drops better
Diffstat:
13 files changed, 140 insertions(+), 163 deletions(-)
diff --git a/src/include/sync/sync_database_lib.h b/src/include/sync/sync_database_lib.h
@@ -37,14 +37,10 @@
* Initialize the sync database subsystem.
*
* @param cfg configuration to use
- * @param skip_preflight true if we should skip the usual
- * preflight check which assures us that the DB is actually
- * operational; only sync-dbinit should use true here.
* @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/
enum GNUNET_GenericReturnValue
-SYNCDB_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
- bool skip_preflight);
+SYNCDB_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
/**
diff --git a/src/sync/sync-httpd.c b/src/sync/sync-httpd.c
@@ -555,8 +555,7 @@ run (void *cls,
}
if (GNUNET_OK !=
- SYNCDB_init (config,
- false))
+ SYNCDB_init (config))
{
global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
diff --git a/src/sync/sync-httpd2.c b/src/sync/sync-httpd2.c
@@ -503,8 +503,7 @@ run (void *cls,
}
if (GNUNET_OK !=
- SYNCDB_init (config,
- false))
+ SYNCDB_init (config))
{
global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
diff --git a/src/syncdb/procedures.sql.in b/src/syncdb/procedures.sql.in
@@ -22,5 +22,6 @@ SET search_path TO sync;
#include "syncdb_lookup_account_TR.sql"
#include "syncdb_store_backup_TR.sql"
#include "syncdb_update_backup_TR.sql"
+#include "syncdb_gc.sql"
COMMIT;
diff --git a/src/syncdb/sync-dbinit.c b/src/syncdb/sync-dbinit.c
@@ -55,8 +55,7 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
if (GNUNET_OK !=
- SYNCDB_init (cfg,
- true))
+ SYNCDB_init (cfg))
{
fprintf (stderr,
"Failed to initialize database.\n");
diff --git a/src/syncdb/syncdb_create_tables.c b/src/syncdb/syncdb_create_tables.c
@@ -24,24 +24,27 @@
enum GNUNET_GenericReturnValue
SYNCDB_create_tables (void)
{
- struct GNUNET_PQ_Context *conn;
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_execute ("SET search_path TO sync;"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
- enum GNUNET_GenericReturnValue ret;
-
- conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
- "syncdb-postgres",
- "sync-",
- es,
- NULL);
- if (NULL == conn)
+ if (GNUNET_SYSERR ==
+ GNUNET_PQ_load_versioning (pg->conn))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_PQ_run_sql (pg->conn,
+ "sync-"))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_PQ_exec_sql (pg->conn,
+ "procedures"))
+ {
+ GNUNET_break (0);
return GNUNET_SYSERR;
- ret = GNUNET_PQ_exec_sql (conn,
- "procedures");
- GNUNET_PQ_disconnect (conn);
- return ret;
+ }
+ return GNUNET_OK;
}
diff --git a/src/syncdb/syncdb_drop_tables.c b/src/syncdb/syncdb_drop_tables.c
@@ -24,25 +24,8 @@
enum GNUNET_GenericReturnValue
SYNCDB_drop_tables (void)
{
- struct GNUNET_PQ_Context *conn;
- enum GNUNET_GenericReturnValue ret;
-
- if (NULL != pg->conn)
- {
- GNUNET_PQ_disconnect (pg->conn);
- pg->conn = NULL;
- }
- conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
- "syncdb-postgres",
- NULL,
- NULL,
- NULL);
- if (NULL == conn)
- return GNUNET_SYSERR;
- ret = GNUNET_PQ_exec_sql (conn,
- "drop");
- GNUNET_PQ_disconnect (conn);
- return ret;
+ return GNUNET_PQ_exec_sql (pg->conn,
+ "drop");
}
diff --git a/src/syncdb/syncdb_gc.c b/src/syncdb/syncdb_gc.c
@@ -27,33 +27,26 @@ SYNCDB_gc (struct GNUNET_TIME_Absolute expire_backups,
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_absolute_time (&expire_backups),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_QueryParam params2[] = {
GNUNET_PQ_query_param_absolute_time (&expire_pending_payments),
GNUNET_PQ_query_param_end
};
- enum GNUNET_DB_QueryStatus qs;
-
- SYNCDB_preflight ();
- PREPARE ("gc_accounts",
- "DELETE FROM accounts "
- "WHERE"
- " expiration_date < $1;");
- PREPARE ("gc_pending_payments",
- "DELETE FROM payments "
- "WHERE"
- " paid=FALSE"
- " AND"
- " timestamp < $1;");
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "gc_accounts",
- params);
- if (qs < 0)
- return qs;
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "gc_pending_payments",
- params2);
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_prepare_anon (
+ pg->conn,
+ "CALL sync.sync_do_gc ($1, $2);"))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (0 > GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "",
+ params))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
}
diff --git a/src/syncdb/syncdb_gc.sql b/src/syncdb/syncdb_gc.sql
@@ -0,0 +1,34 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2026 Taler Systems SA
+--
+-- TALER is free software; you can redistribute it and/or modify it under the
+-- terms of the GNU General Public License as published by the Free Software
+-- Foundation; either version 3, or (at your option) any later version.
+--
+-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+--
+
+DROP PROCEDURE IF EXISTS sync_do_gc;
+CREATE PROCEDURE sync_do_gc(
+ in_expire_backups INT8
+ ,in_expire_pending_payments INT8)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ DELETE
+ FROM accounts
+ WHERE expiration_date < expire_backups;
+ DELETE
+ FROM payments
+ WHERE paid=FALSE
+ AND timestamp < in_expire_pending_payments;
+END $$;
+COMMENT ON PROCEDURE sync_do_gc
+ IS 'Performs garbage collection on sync data';
+COMMIT;
diff --git a/src/syncdb/syncdb_pg.c b/src/syncdb/syncdb_pg.c
@@ -18,6 +18,10 @@
* @brief shared database state and helpers for sync postgres
* @author Christian Grothoff
*/
+
+struct PostgresClosure;
+#define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \
+ struct PostgresClosure
#include "syncdb_pg.h"
#include "sync/sync_util.h"
@@ -25,28 +29,62 @@
/**
* Global database closure.
*/
+// FIXME: prefix!
struct PostgresClosure *pg;
-enum GNUNET_GenericReturnValue
-SYNCDB_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
- bool skip_preflight)
+/**
+ * Function called each time we connect or reconnect to the
+ * database. Gives the application a chance to run some
+ * per-connection initialization logic.
+ *
+ * @param pg_ database conntext in the auditor
+ * @param pq database connection handle
+ */
+static void
+reconnect_cb (struct PostgresClosure *pg_,
+ struct GNUNET_PQ_Context *pq)
{
- pg = GNUNET_new (struct PostgresClosure);
- pg->cfg = cfg;
+#if AUTO_EXPLAIN
+ /* Enable verbose logging to see where queries do not
+ properly use indices */
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
+ GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
+ GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
+ GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
+ /* https://wiki.postgresql.org/wiki/Serializable suggests to really
+ force the default to 'serializable' if SSI is to be used. */
+ GNUNET_PQ_make_try_execute (
+ "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
+ GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
+ GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
+ GNUNET_PQ_make_execute ("SET search_path TO sync;"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+#else
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_execute ("SET search_path TO sync;"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+#endif
+
if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- "syncdb-postgres",
- "SQL_DIR",
- &pg->sql_dir))
+ GNUNET_PQ_exec_statements (pq,
+ es))
{
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "syncdb-postgres",
- "SQL_DIR");
- GNUNET_free (pg);
- pg = NULL;
- return GNUNET_SYSERR;
+ GNUNET_break (0);
+ return;
}
+ pg_->prep_gen++;
+}
+
+
+enum GNUNET_GenericReturnValue
+SYNCDB_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ pg = GNUNET_new (struct PostgresClosure);
+ pg->cfg = cfg;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"sync",
@@ -56,19 +94,17 @@ SYNCDB_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"sync",
"CURRENCY");
- GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
- pg = NULL;
return GNUNET_SYSERR;
}
- if ( (! skip_preflight) &&
- (GNUNET_OK !=
- SYNCDB_preflight ()) )
+ pg->conn = GNUNET_PQ_init (pg->cfg,
+ "merchantdb-postgres",
+ &reconnect_cb,
+ pg);
+ if (NULL == pg->conn)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Database not ready. Try running sync-dbinit!\n");
SYNCDB_fini ();
- return GNUNET_SYSERR;
+ return GNUNET_NO;
}
return GNUNET_OK;
}
@@ -82,9 +118,7 @@ SYNCDB_fini (void)
if (NULL != pg->conn)
GNUNET_PQ_disconnect (pg->conn);
GNUNET_free (pg->currency);
- GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
- pg = NULL;
}
diff --git a/src/syncdb/syncdb_pg.h b/src/syncdb/syncdb_pg.h
@@ -40,11 +40,6 @@ struct PostgresClosure
struct GNUNET_PQ_Context *conn;
/**
- * Directory with SQL statements to run to create tables.
- */
- char *sql_dir;
-
- /**
* Underlying configuration.
*/
const struct GNUNET_CONFIGURATION_Handle *cfg;
diff --git a/src/syncdb/syncdb_preflight.c b/src/syncdb/syncdb_preflight.c
@@ -21,53 +21,6 @@
#include "syncdb_pg.h"
-static enum GNUNET_GenericReturnValue
-internal_setup (void)
-{
- if (NULL == pg->conn)
- {
-#if AUTO_EXPLAIN
- /* Enable verbose logging to see where queries do not
- properly use indices */
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
- GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
- GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
- GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
- /* https://wiki.postgresql.org/wiki/Serializable suggests to really
- force the default to 'serializable' if SSI is to be used. */
- GNUNET_PQ_make_try_execute (
- "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
- GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
- GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
- GNUNET_PQ_make_execute ("SET search_path TO sync;"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
-#else
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_execute ("SET search_path TO sync;"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
-#endif
- struct GNUNET_PQ_Context *db_conn;
-
- db_conn = GNUNET_PQ_connect_with_cfg2 (pg->cfg,
- "syncdb-postgres",
- "sync-",
- es,
- NULL,
- GNUNET_PQ_FLAG_CHECK_CURRENT);
- if (NULL == db_conn)
- return GNUNET_SYSERR;
- pg->conn = db_conn;
- pg->prep_gen++;
- }
- if (NULL == pg->transaction_name)
- GNUNET_PQ_reconnect_if_down (pg->conn);
- return GNUNET_OK;
-}
-
-
enum GNUNET_GenericReturnValue
SYNCDB_preflight (void)
{
@@ -76,22 +29,11 @@ SYNCDB_preflight (void)
GNUNET_PQ_EXECUTE_STATEMENT_END
};
- if (NULL == pg->conn)
- {
- if (GNUNET_OK !=
- internal_setup ())
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to ensure DB is initialized\n");
- return GNUNET_SYSERR;
- }
- }
- else
+ if (NULL == pg->transaction_name)
{
GNUNET_PQ_reconnect_if_down (pg->conn);
- }
- if (NULL == pg->transaction_name)
return GNUNET_OK; /* all good */
+ }
if (GNUNET_OK ==
GNUNET_PQ_exec_statements (pg->conn,
es))
diff --git a/src/syncdb/test_sync_db.c b/src/syncdb/test_sync_db.c
@@ -90,8 +90,7 @@ run (void *cls)
void *b = NULL;
if (GNUNET_OK !=
- SYNCDB_init (cfg,
- true))
+ SYNCDB_init (cfg))
{
result = 77;
return;