@cryptotaxi247 / netdata-1 / commits / 932bbf3b4

Finding leaks No 2 (#19823)

* remove the RRDHOST hostname index * do not use flags for indexing hosts * add dictionary_garbage_collect() to dictionary_flush() * just destroy remaining hot/dirty pages on shutdown * fix dictionaries not adding dictionaries for garbage collect

Costa Tsaousis committed Mar 11, 2025 at 15:36 UTC 932bbf3b4e31eb83bf49201d5e833695d176380d
12 files changed +83 -121
src/daemon/commands.c
+1 -1
@@ -410,7 +410,7 @@ static cmd_status_t cmd_remove_stale_node_internal(char *args, char **message, b
410 RRDHOST *host = NULL;
411 host = rrdhost_find_by_guid(args);
412 if (!host)
413 - host = find_host_by_node_id(args);
413 + host = rrdhost_find_by_node_id(args);
414
415 if (!host) {
416 sqlite3_stmt *res = NULL;
src/daemon/daemon-shutdown.c
+5 -5
@@ -119,7 +119,7 @@ static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collecto
119
120 if(wait_collectors) {
121 size_t running = 1;
122 - size_t count = 10;
122 + size_t count = 50;
123 while (running && count) {
124 running = 0;
125 for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
@@ -272,7 +272,7 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
272 th[tier] = nd_thread_create("rrdeng-exit", NETDATA_THREAD_OPTION_JOINABLE, rrdeng_exit_background, multidb_ctx[tier]);
273
274 // flush anything remaining again - just in case
275 - rrdeng_flush_everything_and_wait(true, false, false);
275 + rrdeng_flush_everything_and_wait(true, true, false);
276
277 for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
278 nd_thread_join(th[tier]);
@@ -334,11 +334,11 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
334
335 // destroy the caches in reverse order (extent and open depend on main cache)
336 fprintf(stderr, "Destroying extent cache (PGC)...\n");
337 - pgc_destroy(extent_cache);
337 + pgc_destroy(extent_cache, false);
338 fprintf(stderr, "Destroying open cache (PGC)...\n");
339 - pgc_destroy(open_cache);
339 + pgc_destroy(open_cache, false);
340 fprintf(stderr, "Destroying main cache (PGC)...\n");
341 - pgc_destroy(main_cache);
341 + pgc_destroy(main_cache, false);
342
343 fprintf(stderr, "Destroying metrics registry (MRG)...\n");
344 size_t metrics_referenced = mrg_destroy(main_mrg);
src/database/contexts/rrdcontext.c
+2 -2
@@ -214,7 +214,7 @@ void rrdcontext_hub_checkpoint_command(void *ptr) {
214 return;
215 }
216
217 - RRDHOST *host = find_host_by_node_id(cmd->node_id);
217 + RRDHOST *host = rrdhost_find_by_node_id(cmd->node_id);
218 if(!host) {
219 nd_log(NDLS_DAEMON, NDLP_WARNING,
220 "RRDCONTEXT: received checkpoint command for claim id '%s', node id '%s', "
@@ -286,7 +286,7 @@ void rrdcontext_hub_stop_streaming_command(void *ptr) {
286 return;
287 }
288
289 - RRDHOST *host = find_host_by_node_id(cmd->node_id);
289 + RRDHOST *host = rrdhost_find_by_node_id(cmd->node_id);
290 if(!host) {
291 nd_log(NDLS_DAEMON, NDLP_WARNING,
292 "RRDCONTEXT: received stop streaming command for claim id '%s', node id '%s', "
src/database/engine/cache.c
+10 -3
@@ -1843,7 +1843,9 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1843
1844 // call the callback to save them
1845 // it may take some time, so let's release the lock
1846 - cache->config.pgc_save_dirty_cb(cache, array, pages, pages_added);
1846 + if(cache->config.pgc_save_dirty_cb)
1847 + cache->config.pgc_save_dirty_cb(cache, array, pages, pages_added);
1848 +
1849 flushes_so_far++;
1850
1851 __atomic_add_fetch(&cache->stats.flushes_completed, pages_added, __ATOMIC_RELAXED);
@@ -2086,7 +2088,12 @@ void pgc_flush_all_hot_and_dirty_pages(PGC *cache, Word_t section) {
2088 flush_pages(cache, 0, section, true, true);
2089 }
2090
2089 -void pgc_destroy(PGC *cache) {
2091 +void pgc_destroy(PGC *cache, bool flush) {
2092 + if(!flush) {
2093 + cache->config.pgc_save_init_cb = NULL;
2094 + cache->config.pgc_save_dirty_cb = NULL;
2095 + }
2096 +
2097 // convert all hot pages to dirty
2098 all_hot_pages_to_dirty(cache, PGC_SECTION_ALL);
2099
@@ -3064,7 +3071,7 @@ int pgc_unittest(void) {
3071 pgc_page_hot_set_end_time_s(cache, page3, 2001, 0);
3072 pgc_page_hot_to_dirty_and_release(cache, page3, false);
3073
3067 - pgc_destroy(cache);
3074 + pgc_destroy(cache, true);
3075
3076 #ifdef PGC_STRESS_TEST
3077 unittest_stress_test();
src/database/engine/cache.h
+1 -1
@@ -175,7 +175,7 @@ PGC *pgc_create(const char *name,
175 PGC_OPTIONS options, size_t partitions, size_t additional_bytes_per_page);
176
177 // destroy the cache
178 -void pgc_destroy(PGC *cache);
178 +void pgc_destroy(PGC *cache, bool flush);
179
180 #define PGC_SECTION_ALL ((Word_t)0)
181 void pgc_flush_dirty_pages(PGC *cache, Word_t section);
src/database/rrdhost.c
+44 -86
@@ -9,7 +9,7 @@
9 RRDHOST *localhost = NULL;
10 netdata_rwlock_t rrd_rwlock = NETDATA_RWLOCK_INITIALIZER;
11
12 -RRDHOST *find_host_by_node_id(char *node_id) {
12 +RRDHOST *rrdhost_find_by_node_id(char *node_id) {
13
14 ND_UUID node_uuid;
15 if (unlikely(!node_id || uuid_parse(node_id, node_uuid.uuid)))
@@ -27,11 +27,30 @@ RRDHOST *find_host_by_node_id(char *node_id) {
27 return ret;
28 }
29
30 +RRDHOST *rrdhost_find_by_hostname(const char *hostname) {
31 + if(strcmp(hostname, "localhost") == 0)
32 + return localhost;
33 +
34 + STRING *name = string_strdupz(hostname);
35 +
36 + RRDHOST *host, *ret = NULL;
37 + dfe_start_read(rrdhost_root_index, host) {
38 + if (host->hostname == name) {
39 + ret = host;
40 + break;
41 + }
42 + }
43 + dfe_done(host);
44 +
45 + string_freez(name);
46 +
47 + return ret;
48 +}
49 +
50 // ----------------------------------------------------------------------------
51 // RRDHOST indexes management
52
53 DICTIONARY *rrdhost_root_index = NULL;
34 -static DICTIONARY *rrdhost_root_index_hostname = NULL;
54
55 void rrdhost_init() {
56 if(unlikely(!rrdhost_root_index)) {
@@ -39,12 +58,6 @@ void rrdhost_init() {
58 DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE,
59 &dictionary_stats_category_rrdhost, 0);
60 }
42 -
43 - if(unlikely(!rrdhost_root_index_hostname)) {
44 - rrdhost_root_index_hostname = dictionary_create_advanced(
45 - DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE,
46 - &dictionary_stats_category_rrdhost, 0);
47 - }
61 }
62
63 RRDHOST_ACQUIRED *rrdhost_find_and_acquire(const char *machine_guid) {
@@ -77,91 +90,36 @@ inline RRDHOST *rrdhost_find_by_guid(const char *guid) {
90 }
91
92 static inline RRDHOST *rrdhost_index_add_by_guid(RRDHOST *host) {
80 - RRDHOST *ret_machine_guid = dictionary_set(rrdhost_root_index, host->machine_guid, host, sizeof(RRDHOST));
81 - if(ret_machine_guid == host)
82 - rrdhost_option_set(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID);
83 - else {
84 - rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID);
85 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
86 - "RRDHOST: host with machine guid '%s' is already indexed. Not adding it again.",
87 - host->machine_guid);
88 - }
89 -
90 - return host;
93 + return dictionary_set(rrdhost_root_index, host->machine_guid, host, sizeof(RRDHOST));
94 }
95
96 static void rrdhost_index_del_by_guid(RRDHOST *host) {
94 - if(rrdhost_option_check(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID)) {
95 - if(!dictionary_del(rrdhost_root_index, host->machine_guid))
97 + RRDHOST *t = rrdhost_find_by_guid(host->machine_guid);
98 + if(t == host) {
99 + if (!dictionary_del(rrdhost_root_index, host->machine_guid))
100 + nd_log(
101 + NDLS_DAEMON, NDLP_NOTICE,
102 + "RRDHOST: failed to delete machine guid '%s' from index",
103 + host->machine_guid);
104 + }
105 + else
106 nd_log(NDLS_DAEMON, NDLP_NOTICE,
97 - "RRDHOST: failed to delete machine guid '%s' from index",
107 + "RRDHOST: failed to delete machine guid '%s' from index, not found",
108 host->machine_guid);
99 -
100 - rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID);
101 - }
102 -}
103 -
104 -// ----------------------------------------------------------------------------
105 -// RRDHOST index by hostname
106 -
107 -inline RRDHOST *rrdhost_find_by_hostname(const char *hostname) {
108 - if(unlikely(!strcmp(hostname, "localhost")))
109 - return localhost;
110 -
111 - RRDHOST *host = dictionary_get(rrdhost_root_index_hostname, hostname);
112 - return host;
113 -}
114 -
115 -static inline void rrdhost_index_del_hostname(RRDHOST *host) {
116 - if(unlikely(!host->hostname)) return;
117 -
118 - if(rrdhost_option_check(host, RRDHOST_OPTION_INDEXED_HOSTNAME)) {
119 - if(!dictionary_del(rrdhost_root_index_hostname, rrdhost_hostname(host)))
120 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
121 - "RRDHOST: failed to delete hostname '%s' from index",
122 - rrdhost_hostname(host));
123 -
124 - rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_HOSTNAME);
125 - }
126 -}
127 -
128 -static inline RRDHOST *rrdhost_index_add_hostname(RRDHOST *host) {
129 - if(!host->hostname) return host;
130 -
131 - RRDHOST *ret_hostname = dictionary_set(rrdhost_root_index_hostname, rrdhost_hostname(host), host, sizeof(RRDHOST));
132 - if(ret_hostname == host)
133 - rrdhost_option_set(host, RRDHOST_OPTION_INDEXED_HOSTNAME);
134 - else {
135 - // have the same hostname, but it's not the same host
136 - // keep the new one only if the old one is orphan or archived
137 - if (rrdhost_flag_check(ret_hostname, RRDHOST_FLAG_ORPHAN) || rrdhost_flag_check(ret_hostname, RRDHOST_FLAG_ARCHIVED)) {
138 - rrdhost_index_del_hostname(ret_hostname);
139 - rrdhost_index_add_hostname(host);
140 - }
141 - else
142 - rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_HOSTNAME);
143 - }
144 -
145 - return host;
109 }
110
111 // ----------------------------------------------------------------------------
112 // RRDHOST - internal helpers
113
151 -static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname, bool add_to_index) {
114 +static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname) {
115 if(unlikely(hostname && !*hostname)) hostname = NULL;
116
117 if(host->hostname && hostname && !strcmp(rrdhost_hostname(host), hostname))
118 return;
119
157 - rrdhost_index_del_hostname(host);
158 -
120 STRING *old = host->hostname;
121 host->hostname = string_strdupz(hostname?hostname:"localhost");
122 string_freez(old);
162 -
163 - if(add_to_index)
164 - rrdhost_index_add_hostname(host);
123 }
124
125 static inline void rrdhost_init_os(RRDHOST *host, const char *os) {
@@ -349,7 +307,7 @@ RRDHOST *rrdhost_create(
307 prog_name,
308 prog_version);
309
352 - rrdhost_init_hostname(host, hostname, false);
310 + rrdhost_init_hostname(host, hostname);
311
312 host->rrd_history_entries = align_entries_to_pagesize(memory_mode, entries);
313 host->health.enabled = ((memory_mode == RRD_DB_MODE_NONE)) ? false : health;
@@ -379,8 +337,7 @@ RRDHOST *rrdhost_create(
337 host->cache_dir = strdupz(netdata_configured_cache_dir);
338
339 // this is also needed for custom host variables - not only health
382 - if(!host->rrdvars)
383 - host->rrdvars = rrdvariables_create();
340 + host->rrdvars = rrdvariables_create();
341
342 if (likely(!uuid_parse(host->machine_guid, host->host_id.uuid)))
343 sql_load_node_id(host);
@@ -454,8 +411,6 @@ RRDHOST *rrdhost_create(
411 return NULL;
412 }
413
457 - rrdhost_index_add_hostname(host);
458 -
414 if(is_localhost)
415 DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(localhost, host, prev, next);
416 else
@@ -560,9 +515,7 @@ static void rrdhost_update(RRDHOST *host
515 "Host '%s' has been renamed to '%s'. If this is not intentional it may mean multiple hosts are using the same machine_guid.",
516 rrdhost_hostname(host), hostname);
517
563 - rrdhost_init_hostname(host, hostname, true);
564 - } else {
565 - rrdhost_index_add_hostname(host);
518 + rrdhost_init_hostname(host, hostname);
519 }
520
521 if(strcmp(rrdhost_program_name(host), prog_name) != 0) {
@@ -814,7 +767,6 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host) {
767 // ------------------------------------------------------------------------
768 // first remove it from the indexes, so that it will not be discoverable
769
817 - rrdhost_index_del_hostname(host);
770 rrdhost_index_del_by_guid(host);
771
772 if (host->prev)
@@ -868,9 +820,15 @@ void rrdhost_free_all(void) {
820
821 localhost = NULL;
822
871 - dictionary_destroy(rrdhost_root_index_hostname);
823 + RRDHOST *host;
824 + dfe_start_write(rrdhost_root_index, host) {
825 + fprintf(stderr, "RRDHOST: MACHINE_GUID '%s' is still in the dictionary!\n",
826 + host_dfe.name);
827 + }
828 + dfe_done(host);
829 +
830 + dictionary_garbage_collect(rrdhost_root_index);
831 dictionary_destroy(rrdhost_root_index);
873 - rrdhost_root_index_hostname = NULL;
832 rrdhost_root_index = NULL;
833
834 rrd_wrunlock();
src/database/rrdhost.h
+5 -9
@@ -94,17 +94,13 @@ typedef enum __attribute__ ((__packed__)) rrdhost_flags {
94 #define rrdhost_flag_set_and_clear(host, set, clear) atomic_flags_set_and_clear(&((host)->flags), set, clear)
95
96 typedef enum __attribute__ ((__packed__)) {
97 - // Indexing
98 - RRDHOST_OPTION_INDEXED_MACHINE_GUID = (1 << 0), // when set, we have indexed its machine guid
99 - RRDHOST_OPTION_INDEXED_HOSTNAME = (1 << 1), // when set, we have indexed its hostname
100 -
97 // Streaming configuration
102 - RRDHOST_OPTION_SENDER_ENABLED = (1 << 2), // set when the host is configured to send metrics to a parent
103 - RRDHOST_OPTION_REPLICATION = (1 << 3), // when set, we support replication for this host
98 + RRDHOST_OPTION_SENDER_ENABLED = (1 << 0), // set when the host is configured to send metrics to a parent
99 + RRDHOST_OPTION_REPLICATION = (1 << 1), // when set, we support replication for this host
100
101 // Other options
106 - RRDHOST_OPTION_VIRTUAL_HOST = (1 << 4), // when set, this host is a virtual one
107 - RRDHOST_OPTION_EPHEMERAL_HOST = (1 << 5), // when set, this host is an ephemeral one
102 + RRDHOST_OPTION_VIRTUAL_HOST = (1 << 2), // when set, this host is a virtual one
103 + RRDHOST_OPTION_EPHEMERAL_HOST = (1 << 3), // when set, this host is an ephemeral one
104 } RRDHOST_OPTIONS;
105
106 #define rrdhost_option_check(host, flag) ((host)->options & (flag))
@@ -388,7 +384,7 @@ void rrdhost_acquired_release(RRDHOST_ACQUIRED *rha);
384
385 RRDHOST *rrdhost_find_by_hostname(const char *hostname);
386 RRDHOST *rrdhost_find_by_guid(const char *guid);
391 -RRDHOST *find_host_by_node_id(char *node_id);
387 +RRDHOST *rrdhost_find_by_node_id(char *node_id);
388
389 #ifdef RRDHOST_INTERNALS
390 RRDHOST *rrdhost_create(
src/database/sqlite/sqlite_aclk_alert.c
+3 -3
@@ -742,7 +742,7 @@ void aclk_push_alert_config_event(char *node_id __maybe_unused, char *config_has
742 sqlite3_stmt *res = NULL;
743 struct aclk_sync_cfg_t *wc;
744
745 - RRDHOST *host = find_host_by_node_id(node_id);
745 + RRDHOST *host = rrdhost_find_by_node_id(node_id);
746
747 if (unlikely(!host || !(wc = host->aclk_config))) {
748 freez(config_hash);
@@ -1053,7 +1053,7 @@ void aclk_start_alert_streaming(char *node_id, uint64_t cloud_version)
1053 return;
1054
1055 struct aclk_sync_cfg_t *wc;
1056 - RRDHOST *host = find_host_by_node_id(node_id);
1056 + RRDHOST *host = rrdhost_find_by_node_id(node_id);
1057
1058 if (unlikely(!host || !(wc = host->aclk_config))) {
1059 nd_log(NDLS_ACCESS, NDLP_NOTICE, "ACLK STA [%s (N/A)]: Ignoring request to stream alert state changes, invalid node.", node_id);
@@ -1087,7 +1087,7 @@ void aclk_alert_version_check(char *node_id, char *claim_id, uint64_t cloud_vers
1087 }
1088
1089 struct aclk_sync_cfg_t *wc;
1090 - RRDHOST *host = find_host_by_node_id(node_id);
1090 + RRDHOST *host = rrdhost_find_by_node_id(node_id);
1091
1092 if ((!host || !(wc = host->aclk_config)))
1093 nd_log(NDLS_ACCESS, NDLP_NOTICE,
src/libnetdata/dictionary/dictionary-unittest.c
+3 -3
@@ -845,8 +845,10 @@ static void *unittest_dict_view_thread(void *arg) {
845 return arg;
846 }
847
848 -static int dictionary_unittest_view_threads() {
848 +static struct dictionary_stats stats_master = { 0 };
849 +static struct dictionary_stats stats_view = { 0 };
850
851 +static int dictionary_unittest_view_threads() {
852 struct thread_view_unittest tv = {
853 .join = 0,
854 .master = NULL,
@@ -856,8 +858,6 @@ static int dictionary_unittest_view_threads() {
858 };
859
860 // threads testing of dictionary
859 - struct dictionary_stats stats_master = {};
860 - struct dictionary_stats stats_view = {};
861 tv.master = dictionary_create_advanced(DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE, &stats_master, 0);
862 tv.view = dictionary_create_view(tv.master);
863 tv.view->stats = &stats_view;
src/libnetdata/dictionary/dictionary.c
+2 -1
@@ -595,6 +595,8 @@ void dictionary_flush(DICTIONARY *dict) {
595 ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE);
596
597 DICTIONARY_STATS_DICT_FLUSHES_PLUS1(dict);
598 +
599 + dictionary_garbage_collect(dict);
600 }
601
602 size_t dictionary_destroy(DICTIONARY *dict) {
@@ -604,7 +606,6 @@ size_t dictionary_destroy(DICTIONARY *dict) {
606
607 ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE);
608
607 - dict_flag_set(dict, DICT_FLAG_DESTROYED);
609 DICTIONARY_STATS_DICT_DESTRUCTIONS_PLUS1(dict);
610
611 size_t referenced_items = dictionary_referenced_items(dict);
src/web/server/h2o/http_server.c
+1 -1
@@ -145,7 +145,7 @@ static inline int _netdata_uberhandler(h2o_req_t *req, RRDHOST **host)
145 if (!*host)
146 *host = rrdhost_find_by_guid(c_host_id);
147 if (!*host)
148 - *host = find_host_by_node_id(c_host_id);
148 + *host = rrdhost_find_by_node_id(c_host_id);
149 if (!*host) {
150 req->res.status = HTTP_RESP_BAD_REQUEST;
151 req->res.reason = "Wrong host id";
src/web/server/web_client.c
+6 -6
@@ -987,19 +987,19 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
987 netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
988
989 if(nodeid) {
990 - host = find_host_by_node_id(tok);
990 + host = rrdhost_find_by_node_id(tok);
991 if(!host) {
992 - host = rrdhost_find_by_hostname(tok);
992 + host = rrdhost_find_by_guid(tok);
993 if (!host)
994 - host = rrdhost_find_by_guid(tok);
994 + host = rrdhost_find_by_hostname(tok);
995 }
996 }
997 else {
998 - host = rrdhost_find_by_hostname(tok);
998 + host = rrdhost_find_by_guid(tok);
999 if(!host) {
1000 - host = rrdhost_find_by_guid(tok);
1000 + host = rrdhost_find_by_node_id(tok);
1001 if (!host)
1002 - host = find_host_by_node_id(tok);
1002 + host = rrdhost_find_by_hostname(tok);
1003 }
1004 }
1005