sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/include/sync/sync_database_lib.h | 6+-----
Msrc/sync/sync-httpd.c | 3+--
Msrc/sync/sync-httpd2.c | 3+--
Msrc/syncdb/procedures.sql.in | 1+
Msrc/syncdb/sync-dbinit.c | 3+--
Msrc/syncdb/syncdb_create_tables.c | 37++++++++++++++++++++-----------------
Msrc/syncdb/syncdb_drop_tables.c | 21++-------------------
Msrc/syncdb/syncdb_gc.c | 41+++++++++++++++++------------------------
Asrc/syncdb/syncdb_gc.sql | 34++++++++++++++++++++++++++++++++++
Msrc/syncdb/syncdb_pg.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/syncdb/syncdb_pg.h | 5-----
Msrc/syncdb/syncdb_preflight.c | 62++------------------------------------------------------------
Msrc/syncdb/test_sync_db.c | 3+--
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;