diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-09-17 00:34:52 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-09-17 00:35:05 +0200 |
commit | 75b3065db390c1c631accd03b68f162953597733 (patch) | |
tree | ff5a5adb1c28a315c66e9898721bfdc5c4440e23 | |
parent | e5e27b880b6df3c4263bca3177050cb6a5ae23a9 (diff) | |
download | anastasis-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.h | 7 | ||||
-rw-r--r-- | src/stasis/anastasis-dbinit.c | 46 | ||||
-rw-r--r-- | src/stasis/plugin_anastasis_postgres.c | 78 |
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; | |||
34 | static int reset_db; | 34 | static int reset_db; |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * -g option: do garbate collection | ||
38 | */ | ||
39 | static 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 | */ | ||
537 | static enum GNUNET_GenericReturnValue | ||
538 | internal_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 | */ |
533 | static void | 593 | static enum GNUNET_GenericReturnValue |
534 | postgres_preflight (void *cls) | 594 | postgres_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 | ||