@cryptotaxi247 / netdata-1 / commits / 39a7a68db

Improve synchronization of dyncfg generated alert configurations to cloud (#21706)

* Track alert configs that we have sent to the cloud either directly or implicitly. On creating an alert via dyncfg check and submit the alert config if needed * Lower alert log level from INFO to DEBUG in SQLite ACLK configuration logging * Add defensive node_id null check USe compiled statements when running from health thread * Explicit init of flag Cleanup old entries on startup

Stelios Fragkakis committed Feb 11, 2026 at 10:19 UTC 39a7a68db1f223983bcb7faa98657db34c2f3ec5
6 files changed +94
src/database/sqlite/sqlite_aclk_alert.c
+76
@@ -508,6 +508,11 @@ static void aclk_push_alert_event(RRDHOST *host, sqlite3_stmt **res, sqlite3_stm
508 while (sqlite3_step_monitored(*res) == SQLITE_ROW) {
509 health_alarm_log_populate(&alarm_log, *res, host, &status);
510 aclk_send_alarm_log_entry(&alarm_log);
511 +
512 + nd_uuid_t hash_id;
513 + if (alarm_log.config_hash && !uuid_parse(alarm_log.config_hash, hash_id))
514 + alert_hash_mark_sent(&hash_id);
515 +
516 aclk_host_config->alert_count++;
517
518 last_id = alarm_log.sequence_id;
@@ -716,6 +721,76 @@ void aclk_push_alert_events_for_all_hosts(void)
721 SQLITE_FINALIZE(res_version);
722 }
723
724 +#define SQL_SELECT_ALERT_HASH_CLOUD "SELECT 1 FROM alert_hash_cloud WHERE hash_id = @hash_id"
725 +#define SQL_INSERT_ALERT_HASH_CLOUD "INSERT OR IGNORE INTO alert_hash_cloud (hash_id) VALUES (@hash_id)"
726 +
727 +void alert_hash_mark_sent(nd_uuid_t *hash_id)
728 +{
729 + if (!hash_id)
730 + return;
731 +
732 + static __thread sqlite3_stmt *compiled_res = NULL;
733 + sqlite3_stmt *res = NULL;
734 +
735 + if (is_health_thread) {
736 + if (!compiled_res) {
737 + if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_INSERT_ALERT_HASH_CLOUD, &compiled_res))
738 + return;
739 + }
740 + res = compiled_res;
741 + } else {
742 + if (!PREPARE_STATEMENT(db_meta, SQL_INSERT_ALERT_HASH_CLOUD, &res))
743 + return;
744 + }
745 +
746 + int param = 0;
747 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, hash_id, sizeof(*hash_id), SQLITE_STATIC));
748 +
749 + param = 0;
750 + (void)sqlite3_step_monitored(res);
751 +
752 +done:
753 + REPORT_BIND_FAIL(res, param);
754 + if (is_health_thread)
755 + SQLITE_RESET(res);
756 + else
757 + SQLITE_FINALIZE(res);
758 +}
759 +
760 +bool alert_hash_has_transitioned(nd_uuid_t *hash_id)
761 +{
762 + if (!hash_id)
763 + return false;
764 +
765 + static __thread sqlite3_stmt *compiled_res = NULL;
766 + sqlite3_stmt *res = NULL;
767 +
768 + if (is_health_thread) {
769 + if (!compiled_res) {
770 + if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_SELECT_ALERT_HASH_CLOUD, &compiled_res))
771 + return false;
772 + }
773 + res = compiled_res;
774 + } else {
775 + if (!PREPARE_STATEMENT(db_meta, SQL_SELECT_ALERT_HASH_CLOUD, &res))
776 + return false;
777 + }
778 +
779 + int param = 0;
780 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, hash_id, sizeof(*hash_id), SQLITE_STATIC));
781 +
782 + param = 0;
783 + bool found = (sqlite3_step_monitored(res) == SQLITE_ROW);
784 +
785 +done:
786 + REPORT_BIND_FAIL(res, param);
787 + if (is_health_thread)
788 + SQLITE_RESET(res);
789 + else
790 + SQLITE_FINALIZE(res);
791 + return found;
792 +}
793 +
794 void aclk_send_alert_configuration(char *config_hash)
795 {
796 if (unlikely(!config_hash))
@@ -841,6 +916,7 @@ void aclk_push_alert_config_event(char *node_id __maybe_unused, char *config_has
916 aclk_host_config->node_id,
917 aclk_host_config->host ? rrdhost_hostname(aclk_host_config->host) : "N/A", config_hash);
918 aclk_send_provide_alarm_cfg(&p_alarm_config);
919 + alert_hash_mark_sent(&hash_uuid);
920 freez(p_alarm_config.cfg_hash);
921 destroy_aclk_alarm_configuration(&alarm_config);
922 }
src/database/sqlite/sqlite_aclk_alert.h
+2
@@ -7,6 +7,8 @@ extern sqlite3 *db_meta;
7
8 void aclk_send_alert_configuration(char *config_hash);
9 void aclk_push_alert_config_event(char *node_id, char *config_hash);
10 +bool alert_hash_has_transitioned(nd_uuid_t *hash_id);
11 +void alert_hash_mark_sent(nd_uuid_t *hash_id);
12 void aclk_start_alert_streaming(char *node_id, uint64_t cloud_version);
13 void aclk_alert_version_check(char *node_id, char *claim_id, uint64_t cloud_version);
14
src/database/sqlite/sqlite_metadata.c
+3
@@ -104,6 +104,8 @@ const char *database_config[] = {
104 "CREATE TABLE IF NOT EXISTS aclk_queue (sequence_id INTEGER PRIMARY KEY, host_id blob, health_log_id INT, "
105 "unique_id INT, date_created INT, UNIQUE(host_id, health_log_id))",
106
107 + "CREATE TABLE IF NOT EXISTS alert_hash_cloud (hash_id BLOB PRIMARY KEY)",
108 +
109 "CREATE TABLE IF NOT EXISTS ctx_metadata_cleanup (id INTEGER PRIMARY KEY, host_id BLOB, context TEXT NOT NULL, date_created INT NOT NULL, "
110 "UNIQUE (host_id, context))",
111
@@ -124,6 +126,7 @@ const char *database_cleanup[] = {
126 "DROP INDEX IF EXISTS health_log_d_ind_4",
127 "DROP INDEX IF EXISTS health_log_d_ind_1",
128 "DROP INDEX IF EXISTS health_log_d_ind_5",
129 + "DELETE FROM alert_hash_cloud WHERE hash_id NOT IN (SELECT hash_id FROM alert_hash)",
130 NULL
131 };
132
src/health/health.c
+1
@@ -24,6 +24,7 @@ struct health_plugin_globals health_globals = {
24 },
25 .prototypes = {
26 .dict = NULL,
27 + .registering = false,
28 }
29 };
30
src/health/health_internals.h
+1
@@ -92,6 +92,7 @@ struct health_plugin_globals {
92
93 struct {
94 DICTIONARY *dict;
95 + bool registering;
96 } prototypes;
97 };
98
src/health/health_prototypes.c
+11
@@ -2,6 +2,7 @@
2
3 #include "health_internals.h"
4 #include "health-alert-entry.h"
5 +#include "database/sqlite/sqlite_aclk_alert.h"
6
7 // ---------------------------------------------------------------------------------------------------------------------
8
@@ -388,6 +389,14 @@ void health_prototype_hash_id(RRD_ALERT_PROTOTYPE *ap) {
389 uuid_copy(ap->config.hash_id, uuid.uuid);
390
391 sql_alert_store_config(ap);
392 +
393 + if (ap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG &&
394 + !__atomic_load_n(&health_globals.prototypes.registering, __ATOMIC_RELAXED) &&
395 + !alert_hash_has_transitioned(&ap->config.hash_id)) {
396 + char hash_id_str[UUID_STR_LEN];
397 + uuid_unparse_lower(ap->config.hash_id, hash_id_str);
398 + aclk_send_alert_configuration(hash_id_str);
399 + }
400 }
401
402 bool health_prototype_add(RRD_ALERT_PROTOTYPE *ap, char **msg) {
@@ -490,7 +499,9 @@ void health_reload_prototypes(void) {
499 NULL, 0);
500
501 // register all loaded prototypes
502 + __atomic_store_n(&health_globals.prototypes.registering, true, __ATOMIC_RELAXED);
503 health_dyncfg_register_all_prototypes();
504 + __atomic_store_n(&health_globals.prototypes.registering, false, __ATOMIC_RELAXED);
505 }
506
507 // ---------------------------------------------------------------------------------------------------------------------