aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-09-17 00:34:52 +0200
committerChristian Grothoff <christian@grothoff.org>2021-09-17 00:35:05 +0200
commit75b3065db390c1c631accd03b68f162953597733 (patch)
treeff5a5adb1c28a315c66e9898721bfdc5c4440e23
parente5e27b880b6df3c4263bca3177050cb6a5ae23a9 (diff)
downloadanastasis-75b3065db390c1c631accd03b68f162953597733.tar.gz
anastasis-75b3065db390c1c631accd03b68f162953597733.zip
expose DB garbage collection, ROLLBACK instead of committing if preflight check fails
-rw-r--r--src/include/anastasis_database_plugin.h7
-rw-r--r--src/stasis/anastasis-dbinit.c46
-rw-r--r--src/stasis/plugin_anastasis_postgres.c78
3 files changed, 119 insertions, 12 deletions
diff --git a/src/include/anastasis_database_plugin.h b/src/include/anastasis_database_plugin.h
index bc4b0e6..7bf91a2 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -232,9 +232,12 @@ struct ANASTASIS_DatabasePlugin
232 * Does not return anything, as we will continue regardless of the outcome. 232 * Does not return anything, as we will continue regardless of the outcome.
233 * 233 *
234 * @param cls the `struct PostgresClosure` with the plugin-specific state 234 * @param cls the `struct PostgresClosure` with the plugin-specific state
235 * @return #GNUNET_OK if everything is fine
236 * #GNUNET_NO if a transaction was rolled back
237 * #GNUNET_SYSERR on hard errors
235 */ 238 */
236 void 239 enum GNUNET_GenericReturnValue
237 (*preflight) (void *cls); 240 (*preflight)(void *cls);
238 241
239 /** 242 /**
240 * Check that the database connection is still up. 243 * Check that the database connection is still up.
diff --git a/src/stasis/anastasis-dbinit.c b/src/stasis/anastasis-dbinit.c
index 17b3c56..a0e17db 100644
--- a/src/stasis/anastasis-dbinit.c
+++ b/src/stasis/anastasis-dbinit.c
@@ -34,6 +34,11 @@ static int global_ret;
34static int reset_db; 34static int reset_db;
35 35
36/** 36/**
37 * -g option: do garbate collection
38 */
39static int gc_db;
40
41/**
37 * Main function that will be run. 42 * Main function that will be run.
38 * 43 *
39 * @param cls closure 44 * @param cls closure
@@ -54,21 +59,49 @@ run (void *cls,
54 { 59 {
55 fprintf (stderr, 60 fprintf (stderr,
56 "Failed to initialize database plugin.\n"); 61 "Failed to initialize database plugin.\n");
57 global_ret = EXIT_FAILURE; 62 global_ret = EXIT_NOTINSTALLED;
58 return; 63 return;
59 } 64 }
60 if (reset_db) 65 if (reset_db)
61 { 66 {
62 (void) plugin->drop_tables (plugin->cls); 67 if (GNUNET_OK != plugin->drop_tables (plugin->cls))
63 ANASTASIS_DB_plugin_unload (plugin); 68 {
64 plugin = ANASTASIS_DB_plugin_load (cfg); 69 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
70 "Could not drop tables as requested. Either database was not yet initialized, or permission denied. Consult the logs. Will still try to create new tables.\n");
71 }
65 } 72 }
66 if (GNUNET_OK != 73 if (GNUNET_OK !=
67 plugin->create_tables (plugin->cls)) 74 plugin->create_tables (plugin->cls))
68 { 75 {
69 global_ret = EXIT_FAILURE; 76 global_ret = EXIT_FAILURE;
77 ANASTASIS_DB_plugin_unload (plugin);
70 return; 78 return;
71 } 79 }
80 if (gc_db)
81 {
82 struct GNUNET_TIME_Absolute expire_backups;
83 struct GNUNET_TIME_Absolute expire_payments;
84 struct GNUNET_TIME_Absolute now;
85
86 now = GNUNET_TIME_absolute_get ();
87 expire_backups = GNUNET_TIME_absolute_subtract (
88 now,
89 GNUNET_TIME_relative_multiply (
90 GNUNET_TIME_UNIT_MONTHS,
91 6));
92 expire_payments = GNUNET_TIME_absolute_subtract (
93 now,
94 GNUNET_TIME_relative_multiply (
95 GNUNET_TIME_UNIT_YEARS,
96 10));
97 if (0 > plugin->gc (plugin->cls,
98 expire_backups,
99 expire_payments))
100 {
101 fprintf (stderr,
102 "Garbage collection failed!\n");
103 }
104 }
72 ANASTASIS_DB_plugin_unload (plugin); 105 ANASTASIS_DB_plugin_unload (plugin);
73} 106}
74 107
@@ -86,7 +119,10 @@ main (int argc,
86 char *const *argv) 119 char *const *argv)
87{ 120{
88 struct GNUNET_GETOPT_CommandLineOption options[] = { 121 struct GNUNET_GETOPT_CommandLineOption options[] = {
89 122 GNUNET_GETOPT_option_flag ('g',
123 "garbagecollect",
124 "remove state data from database",
125 &gc_db),
90 GNUNET_GETOPT_option_flag ('r', 126 GNUNET_GETOPT_option_flag ('r',
91 "reset", 127 "reset",
92 "reset database (DANGEROUS: all existing data is lost!)", 128 "reset database (DANGEROUS: all existing data is lost!)",
diff --git a/src/stasis/plugin_anastasis_postgres.c b/src/stasis/plugin_anastasis_postgres.c
index b78dbdb..b1be081 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -72,6 +72,10 @@ struct PostgresClosure
72 */ 72 */
73 char *currency; 73 char *currency;
74 74
75 /**
76 * Prepared statements have been initialized.
77 */
78 bool init;
75}; 79};
76 80
77 81
@@ -524,38 +528,102 @@ check_connection (void *cls)
524 528
525 529
526/** 530/**
531 * Connect to the database if the connection does not exist yet.
532 *
533 * @param pg the plugin-specific state
534 * @param skip_prepare true if we should skip prepared statement setup
535 * @return #GNUNET_OK on success
536 */
537static enum GNUNET_GenericReturnValue
538internal_setup (struct PostgresClosure *pg,
539 bool skip_prepare)
540{
541 if (NULL == pg->conn)
542 {
543#if AUTO_EXPLAIN
544 /* Enable verbose logging to see where queries do not
545 properly use indices */
546 struct GNUNET_PQ_ExecuteStatement es[] = {
547 GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
548 GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
549 GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
550 GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
551 /* https://wiki.postgresql.org/wiki/Serializable suggests to really
552 force the default to 'serializable' if SSI is to be used. */
553 GNUNET_PQ_make_try_execute (
554 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
555 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
556 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
557 GNUNET_PQ_EXECUTE_STATEMENT_END
558 };
559#else
560 struct GNUNET_PQ_ExecuteStatement *es = NULL;
561#endif
562 struct GNUNET_PQ_Context *db_conn;
563
564 db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
565 "exchangedb-postgres",
566 NULL,
567 es,
568 NULL);
569 if (NULL == db_conn)
570 return GNUNET_SYSERR;
571 pg->conn = db_conn;
572 }
573 if (NULL == pg->transaction_name)
574 GNUNET_PQ_reconnect_if_down (pg->conn);
575 if (pg->init)
576 return GNUNET_OK;
577 if (skip_prepare)
578 return GNUNET_OK;
579 return postgres_connect (pg);
580}
581
582
583/**
527 * Do a pre-flight check that we are not in an uncommitted transaction. 584 * Do a pre-flight check that we are not in an uncommitted transaction.
528 * If we are, try to commit the previous transaction and output a warning. 585 * If we are, try to commit the previous transaction and output a warning.
529 * Does not return anything, as we will continue regardless of the outcome. 586 * Does not return anything, as we will continue regardless of the outcome.
530 * 587 *
531 * @param cls the `struct PostgresClosure` with the plugin-specific state 588 * @param cls the `struct PostgresClosure` with the plugin-specific state
589 * @return #GNUNET_OK if everything is fine
590 * #GNUNET_NO if a transaction was rolled back
591 * #GNUNET_SYSERR on hard errors
532 */ 592 */
533static void 593static enum GNUNET_GenericReturnValue
534postgres_preflight (void *cls) 594postgres_preflight (void *cls)
535{ 595{
536 struct PostgresClosure *pg = cls; 596 struct PostgresClosure *pg = cls;
537 struct GNUNET_PQ_ExecuteStatement es[] = { 597 struct GNUNET_PQ_ExecuteStatement es[] = {
538 GNUNET_PQ_make_execute ("COMMIT"), 598 GNUNET_PQ_make_execute ("ROLLBACK"),
539 GNUNET_PQ_EXECUTE_STATEMENT_END 599 GNUNET_PQ_EXECUTE_STATEMENT_END
540 }; 600 };
541 601
602 if (! pg->init)
603 {
604 if (GNUNET_OK !=
605 internal_setup (pg,
606 false))
607 return GNUNET_SYSERR;
608 }
542 if (NULL == pg->transaction_name) 609 if (NULL == pg->transaction_name)
543 return; /* all good */ 610 return GNUNET_OK; /* all good */
544 if (GNUNET_OK == 611 if (GNUNET_OK ==
545 GNUNET_PQ_exec_statements (pg->conn, 612 GNUNET_PQ_exec_statements (pg->conn,
546 es)) 613 es))
547 { 614 {
548 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 615 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
549 "BUG: Preflight check committed transaction `%s'!\n", 616 "BUG: Preflight check rolled back transaction `%s'!\n",
550 pg->transaction_name); 617 pg->transaction_name);
551 } 618 }
552 else 619 else
553 { 620 {
554 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 621 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
555 "BUG: Preflight check failed to commit transaction `%s'!\n", 622 "BUG: Preflight check failed to rollback transaction `%s'!\n",
556 pg->transaction_name); 623 pg->transaction_name);
557 } 624 }
558 pg->transaction_name = NULL; 625 pg->transaction_name = NULL;
626 return GNUNET_NO;
559} 627}
560 628
561 629