From 112b5b91647c3dea45cc1c9bc364df526c8012f1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 26 Jan 2022 19:05:15 +0100 Subject: Merging upstream version 1.33.0. Signed-off-by: Daniel Baumann --- database/engine/README.md | 6 +++--- database/engine/pagecache.c | 6 +++--- database/engine/rrdengine.c | 18 ++++++++--------- database/engine/rrdengineapi.c | 12 +++++------ database/engine/rrdenginelib.h | 40 ------------------------------------- database/rrd.h | 14 +++++++------ database/rrdhost.c | 17 ++++++++++++++++ database/rrdset.c | 9 ++++++++- database/sqlite/sqlite3.c | 2 +- database/sqlite/sqlite_aclk.c | 18 +++++++++++++++++ database/sqlite/sqlite_aclk.h | 1 + database/sqlite/sqlite_aclk_alert.c | 30 ++++++++++++++++++++++++---- database/sqlite/sqlite_aclk_chart.c | 8 ++++++-- database/sqlite/sqlite_aclk_node.c | 4 ++++ 14 files changed, 110 insertions(+), 75 deletions(-) (limited to 'database') diff --git a/database/engine/README.md b/database/engine/README.md index 191366a46..a782716f0 100644 --- a/database/engine/README.md +++ b/database/engine/README.md @@ -44,7 +44,7 @@ section for details. The `dbengine multihost disk space` option determines the amount of disk space in **MiB** that is dedicated to storing Netdata metric values and all related metadata describing them. You can use the [**database engine -calculator**](/docs/store/change-metrics-storage.md#calculate-the-system-resources-RAM-disk-space-needed-to-store-metrics) +calculator**](/docs/store/change-metrics-storage.md#calculate-the-system-resources-ram-disk-space-needed-to-store-metrics) to correctly set `dbengine multihost disk space` based on your metrics retention policy. The calculator gives an accurate estimate based on how many child nodes you have, how many metrics your Agent collects, and more. @@ -62,7 +62,7 @@ Netdata metric values per legacy database engine instance (see [details on the l When using the multihost database engine, all parent and child nodes share the same `page cache size` and `dbengine multihost disk space` in a single dbengine instance. The [**database engine -calculator**](/docs/store/change-metrics-storage.md#calculate-the-system-resources-RAM-disk-space-needed-to-store-metrics) +calculator**](/docs/store/change-metrics-storage.md#calculate-the-system-resources-ram-disk-space-needed-to-store-metrics) helps you properly set `page cache size` and `dbengine multihost disk space` on your parent node to allocate enough resources based on your metrics retention policy and how many child nodes you have. @@ -112,7 +112,7 @@ An important observation is that RAM usage depends on both the `page cache size` options. You can use our [database engine -calculator](/docs/store/change-metrics-storage.md#calculate-the-system-resources-RAM-disk-space-needed-to-store-metrics) +calculator](/docs/store/change-metrics-storage.md#calculate-the-system-resources-ram-disk-space-needed-to-store-metrics) to validate the memory requirements for your particular system(s) and configuration (**out-of-date**). ### Disk space requirements diff --git a/database/engine/pagecache.c b/database/engine/pagecache.c index 90423176c..40e24b321 100644 --- a/database/engine/pagecache.c +++ b/database/engine/pagecache.c @@ -289,14 +289,14 @@ static void pg_cache_reserve_pages(struct rrdengine_instance *ctx, unsigned numb ++failures; uv_rwlock_wrunlock(&pg_cache->pg_cache_rwlock); - init_completion(&compl); + completion_init(&compl); cmd.opcode = RRDENG_FLUSH_PAGES; cmd.completion = &compl; rrdeng_enq_cmd(&ctx->worker_config, &cmd); /* wait for some pages to be flushed */ debug(D_RRDENGINE, "%s: waiting for pages to be written to disk before evicting.", __func__); - wait_for_completion(&compl); - destroy_completion(&compl); + completion_wait_for(&compl); + completion_destroy(&compl); if (unlikely(failures > 1)) { unsigned long slots, usecs_to_sleep; diff --git a/database/engine/rrdengine.c b/database/engine/rrdengine.c index 54a9cdf8d..a975cfa6e 100644 --- a/database/engine/rrdengine.c +++ b/database/engine/rrdengine.c @@ -207,7 +207,7 @@ void read_cached_extent_cb(struct rrdengine_worker_config* wc, unsigned idx, str } } if (xt_io_descr->completion) - complete(xt_io_descr->completion); + completion_mark_complete(xt_io_descr->completion); freez(xt_io_descr); } @@ -360,7 +360,7 @@ after_crc_check: freez(uncompressed_buf); } if (xt_io_descr->completion) - complete(xt_io_descr->completion); + completion_mark_complete(xt_io_descr->completion); uv_fs_req_cleanup(req); free(xt_io_descr->buf); freez(xt_io_descr); @@ -634,7 +634,7 @@ void flush_pages_cb(uv_fs_t* req) rrdeng_page_descr_mutex_unlock(ctx, descr); } if (xt_io_descr->completion) - complete(xt_io_descr->completion); + completion_mark_complete(xt_io_descr->completion); uv_fs_req_cleanup(req); free(xt_io_descr->buf); freez(xt_io_descr); @@ -712,7 +712,7 @@ static int do_flush_pages(struct rrdengine_worker_config* wc, int force, struct if (!count) { debug(D_RRDENGINE, "%s: no pages eligible for flushing.", __func__); if (completion) - complete(completion); + completion_mark_complete(completion); return 0; } wc->inflight_dirty_pages += count; @@ -975,7 +975,7 @@ static void rrdeng_cleanup_finished_threads(struct rrdengine_worker_config* wc) } if (unlikely(SET_QUIESCE == ctx->quiesce && !rrdeng_threads_alive(wc))) { ctx->quiesce = QUIESCED; - complete(&ctx->rrdengine_completion); + completion_mark_complete(&ctx->rrdengine_completion); } } @@ -1171,7 +1171,7 @@ void rrdeng_worker(void* arg) wc->error = 0; /* wake up initialization thread */ - complete(&ctx->rrdengine_completion); + completion_mark_complete(&ctx->rrdengine_completion); fatal_assert(0 == uv_timer_start(&timer_req, timer_cb, TIMER_PERIOD_MS, TIMER_PERIOD_MS)); shutdown = 0; @@ -1211,7 +1211,7 @@ void rrdeng_worker(void* arg) wal_flush_transaction_buffer(wc); if (!rrdeng_threads_alive(wc)) { ctx->quiesce = QUIESCED; - complete(&ctx->rrdengine_completion); + completion_mark_complete(&ctx->rrdengine_completion); } break; case RRDENG_READ_PAGE: @@ -1226,7 +1226,7 @@ void rrdeng_worker(void* arg) case RRDENG_FLUSH_PAGES: { if (wc->now_invalidating_dirty_pages) { /* Do not flush if the disk cannot keep up */ - complete(cmd.completion); + completion_mark_complete(cmd.completion); } else { (void)do_flush_pages(wc, 1, cmd.completion); } @@ -1276,7 +1276,7 @@ error_after_loop_init: wc->error = UV_EAGAIN; /* wake up initialization thread */ - complete(&ctx->rrdengine_completion); + completion_mark_complete(&ctx->rrdengine_completion); } /* C entry point for development purposes diff --git a/database/engine/rrdengineapi.c b/database/engine/rrdengineapi.c index d81b95805..6ebee1459 100755 --- a/database/engine/rrdengineapi.c +++ b/database/engine/rrdengineapi.c @@ -944,11 +944,11 @@ int rrdeng_init(RRDHOST *host, struct rrdengine_instance **ctxp, char *dbfiles_p goto error_after_init_rrd_files; } - init_completion(&ctx->rrdengine_completion); + completion_init(&ctx->rrdengine_completion); fatal_assert(0 == uv_thread_create(&ctx->worker_config.thread, rrdeng_worker, &ctx->worker_config)); /* wait for worker thread to initialize */ - wait_for_completion(&ctx->rrdengine_completion); - destroy_completion(&ctx->rrdengine_completion); + completion_wait_for(&ctx->rrdengine_completion); + completion_destroy(&ctx->rrdengine_completion); uv_thread_set_name_np(ctx->worker_config.thread, "DBENGINE"); if (ctx->worker_config.error) { goto error_after_rrdeng_worker; @@ -1009,13 +1009,13 @@ void rrdeng_prepare_exit(struct rrdengine_instance *ctx) return; } - init_completion(&ctx->rrdengine_completion); + completion_init(&ctx->rrdengine_completion); cmd.opcode = RRDENG_QUIESCE; rrdeng_enq_cmd(&ctx->worker_config, &cmd); /* wait for dbengine to quiesce */ - wait_for_completion(&ctx->rrdengine_completion); - destroy_completion(&ctx->rrdengine_completion); + completion_wait_for(&ctx->rrdengine_completion); + completion_destroy(&ctx->rrdengine_completion); //metalog_prepare_exit(ctx->metalog_ctx); } diff --git a/database/engine/rrdenginelib.h b/database/engine/rrdenginelib.h index 8b6751f00..32eebf103 100644 --- a/database/engine/rrdenginelib.h +++ b/database/engine/rrdenginelib.h @@ -14,9 +14,6 @@ struct rrdengine_instance; #define BITS_PER_ULONG (sizeof(unsigned long) * 8) -/* Taken from linux kernel */ -#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) - #define ALIGN_BYTES_FLOOR(x) (((x) / RRDENG_BLOCK_SIZE) * RRDENG_BLOCK_SIZE) #define ALIGN_BYTES_CEILING(x) ((((x) + RRDENG_BLOCK_SIZE - 1) / RRDENG_BLOCK_SIZE) * RRDENG_BLOCK_SIZE) @@ -76,43 +73,6 @@ static inline unsigned long ulong_compare_and_swap(volatile unsigned long *ptr, #define O_DIRECT (0) #endif -struct completion { - uv_mutex_t mutex; - uv_cond_t cond; - volatile unsigned completed; -}; - -static inline void init_completion(struct completion *p) -{ - p->completed = 0; - fatal_assert(0 == uv_cond_init(&p->cond)); - fatal_assert(0 == uv_mutex_init(&p->mutex)); -} - -static inline void destroy_completion(struct completion *p) -{ - uv_cond_destroy(&p->cond); - uv_mutex_destroy(&p->mutex); -} - -static inline void wait_for_completion(struct completion *p) -{ - uv_mutex_lock(&p->mutex); - while (0 == p->completed) { - uv_cond_wait(&p->cond, &p->mutex); - } - fatal_assert(1 == p->completed); - uv_mutex_unlock(&p->mutex); -} - -static inline void complete(struct completion *p) -{ - uv_mutex_lock(&p->mutex); - p->completed = 1; - uv_mutex_unlock(&p->mutex); - uv_cond_broadcast(&p->cond); -} - static inline int crc32cmp(void *crcp, uLong crc) { return (*(uint32_t *)crcp != crc); diff --git a/database/rrd.h b/database/rrd.h index 7f8b91f7d..3e0daa169 100644 --- a/database/rrd.h +++ b/database/rrd.h @@ -754,6 +754,8 @@ struct rrdhost_system_info { char *container_detection; char *is_k8s_node; uint16_t hops; + bool ml_capable; + bool ml_enabled; }; struct rrdhost { @@ -799,22 +801,22 @@ struct rrdhost { // ------------------------------------------------------------------------ // streaming of data to remote hosts - rrdpush - unsigned int rrdpush_send_enabled:1; // 1 when this host sends metrics to another netdata + unsigned int rrdpush_send_enabled; // 1 when this host sends metrics to another netdata char *rrdpush_send_destination; // where to send metrics to char *rrdpush_send_api_key; // the api key at the receiving netdata // the following are state information for the threading // streaming metrics from this netdata to an upstream netdata struct sender_state *sender; - volatile unsigned int rrdpush_sender_spawn:1; // 1 when the sender thread has been spawn + volatile unsigned int rrdpush_sender_spawn; // 1 when the sender thread has been spawn netdata_thread_t rrdpush_sender_thread; // the sender thread void *dbsync_worker; - volatile unsigned int rrdpush_sender_connected:1; // 1 when the sender is ready to push metrics + volatile unsigned int rrdpush_sender_connected; // 1 when the sender is ready to push metrics int rrdpush_sender_socket; // the fd of the socket to the remote host, or -1 - volatile unsigned int rrdpush_sender_error_shown:1; // 1 when we have logged a communication error - volatile unsigned int rrdpush_sender_join:1; // 1 when we have to join the sending thread + volatile unsigned int rrdpush_sender_error_shown; // 1 when we have logged a communication error + volatile unsigned int rrdpush_sender_join; // 1 when we have to join the sending thread SIMPLE_PATTERN *rrdpush_send_charts_matching; // pattern to match the charts to be sent @@ -837,7 +839,7 @@ struct rrdhost { // ------------------------------------------------------------------------ // health monitoring options - unsigned int health_enabled:1; // 1 when this host has health enabled + unsigned int health_enabled; // 1 when this host has health enabled time_t health_delay_up_to; // a timestamp to delay alarms processing up to char *health_default_exec; // the full path of the alarms notifications program char *health_default_recipient; // the default recipient for all alarms diff --git a/database/rrdhost.c b/database/rrdhost.c index d9608b740..79e283a63 100644 --- a/database/rrdhost.c +++ b/database/rrdhost.c @@ -382,7 +382,19 @@ RRDHOST *rrdhost_create(const char *hostname, else localhost = host; } + // ------------------------------------------------------------------------ + // init new ML host and update system_info to let upstreams know + // about ML functionality + ml_new_host(host); + if (is_localhost && host->system_info) { +#ifndef ENABLE_ML + host->system_info->ml_capable = 0; +#else + host->system_info->ml_capable = 1; +#endif + host->system_info->ml_enabled = host->ml_host != NULL; + } info("Host '%s' (at registry as '%s') with guid '%s' initialized" ", os '%s'" @@ -839,6 +851,10 @@ void rrdhost_free(RRDHOST *host) { rrdpush_sender_thread_stop(host); // stop a possibly running thread cbuffer_free(host->sender->buffer); buffer_free(host->sender->build); +#ifdef ENABLE_COMPRESSION + if (host->sender->compressor) + host->sender->compressor->destroy(&host->sender->compressor); +#endif freez(host->sender); host->sender = NULL; if (netdata_exit) { @@ -938,6 +954,7 @@ void rrdhost_free(RRDHOST *host) { pthread_mutex_destroy(&host->aclk_state_lock); freez(host->aclk_state.claimed_id); + freez(host->aclk_state.prev_claimed_id); freez((void *)host->tags); free_label_list(host->labels.head); freez((void *)host->os); diff --git a/database/rrdset.c b/database/rrdset.c index 2d3ee9609..19af449d8 100644 --- a/database/rrdset.c +++ b/database/rrdset.c @@ -1453,7 +1453,14 @@ void rrdset_done(RRDSET *st) { // check if we will re-write the entire data set if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut && st->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE)) { - info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Resetting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec); + info( + "%s: too old data (last updated at %"PRId64".%"PRId64", last collected at %"PRId64".%"PRId64"). " + "Resetting it. Will not store the next entry.", + st->name, + (int64_t)st->last_updated.tv_sec, + (int64_t)st->last_updated.tv_usec, + (int64_t)st->last_collected_time.tv_sec, + (int64_t)st->last_collected_time.tv_usec); rrdset_reset(st); rrdset_init_last_updated_time(st); diff --git a/database/sqlite/sqlite3.c b/database/sqlite/sqlite3.c index 057ce52d8..61a74f059 100644 --- a/database/sqlite/sqlite3.c +++ b/database/sqlite/sqlite3.c @@ -124427,7 +124427,7 @@ insert_cleanup: /* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn(). * Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this ** expression node references any of the -** columns that are being modifed by an UPDATE statement. +** columns that are being modified by an UPDATE statement. */ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_COLUMN ){ diff --git a/database/sqlite/sqlite_aclk.c b/database/sqlite/sqlite_aclk.c index 6803092f2..63196a81e 100644 --- a/database/sqlite/sqlite_aclk.c +++ b/database/sqlite/sqlite_aclk.c @@ -189,6 +189,24 @@ int aclk_worker_enq_cmd(char *node_id, struct aclk_database_cmd *cmd) return (wc == NULL); } +struct aclk_database_worker_config *find_inactive_wc_by_node_id(char *node_id) +{ + if (unlikely(!node_id)) + return NULL; + + uv_mutex_lock(&aclk_async_lock); + struct aclk_database_worker_config *wc = aclk_thread_head; + + while (wc) { + if (!strcmp(wc->node_id, node_id)) + break; + wc = wc->next; + } + uv_mutex_unlock(&aclk_async_lock); + + return (wc); +} + void aclk_sync_exit_all() { rrd_wrlock(); diff --git a/database/sqlite/sqlite_aclk.h b/database/sqlite/sqlite_aclk.h index d554e1069..424949740 100644 --- a/database/sqlite/sqlite_aclk.h +++ b/database/sqlite/sqlite_aclk.h @@ -229,4 +229,5 @@ void sql_delete_aclk_table_list(struct aclk_database_worker_config *wc, struct a void sql_maint_aclk_sync_database(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); int claimed(); void aclk_sync_exit_all(); +struct aclk_database_worker_config *find_inactive_wc_by_node_id(char *node_id); #endif //NETDATA_SQLITE_ACLK_H diff --git a/database/sqlite/sqlite_aclk_alert.c b/database/sqlite/sqlite_aclk_alert.c index 8d7694244..238b500a8 100644 --- a/database/sqlite/sqlite_aclk_alert.c +++ b/database/sqlite/sqlite_aclk_alert.c @@ -209,7 +209,7 @@ void aclk_push_alert_event(struct aclk_database_worker_config *wc, struct aclk_d char *edit_command = sqlite3_column_bytes(res, 16) > 0 ? health_edit_command_from_source((char *)sqlite3_column_text(res, 16)) : - strdupz("UNKNOWN=0"); + strdupz("UNKNOWN=0=UNKNOWN"); alarm_log.command = strdupz(edit_command); alarm_log.duration = (time_t) sqlite3_column_int64(res, 6); @@ -321,6 +321,22 @@ void aclk_push_alarm_health_log(struct aclk_database_worker_config *wc, struct a if (unlikely(!claim_id)) return; + RRDHOST *host = wc->host; + if (unlikely(!host)) { + rrd_wrlock(); + host = find_host_by_node_id(wc->node_id); + rrd_unlock(); + + if (unlikely(!host)) { + log_access( + "AC [%s (N/A)]: ACLK synchronization thread for %s is not yet linked to HOST.", + wc->node_id, + wc->host_guid); + freez(claim_id); + return; + } + } + uint64_t first_sequence = 0; uint64_t last_sequence = 0; struct timeval first_timestamp; @@ -370,7 +386,7 @@ void aclk_push_alarm_health_log(struct aclk_database_worker_config *wc, struct a alarm_log.node_id = wc->node_id; alarm_log.log_entries = log_entries; alarm_log.status = wc->alert_updates == 0 ? 2 : 1; - alarm_log.enabled = 1; + alarm_log.enabled = (int)host->health_enabled; wc->alert_sequence_id = last_sequence; @@ -546,7 +562,9 @@ void aclk_start_alert_streaming(char *node_id, uint64_t batch_id, uint64_t start rrd_wrlock(); RRDHOST *host = find_host_by_node_id(node_id); if (likely(host)) - wc = (struct aclk_database_worker_config *)host->dbsync_worker; + wc = (struct aclk_database_worker_config *)host->dbsync_worker ? + (struct aclk_database_worker_config *)host->dbsync_worker : + (struct aclk_database_worker_config *)find_inactive_wc_by_node_id(node_id); rrd_unlock(); if (unlikely(!host->health_enabled)) { @@ -589,6 +607,8 @@ void sql_process_queue_removed_alerts_to_aclk(struct aclk_database_worker_config db_execute(buffer_tostring(sql)); + log_access("AC [%s (%s)]: Queued removed alerts.", wc->node_id, wc->host ? wc->host->hostname : "N/A"); + buffer_free(sql); #endif return; @@ -677,7 +697,7 @@ void aclk_mark_alert_cloud_ack(char *uuid_str, uint64_t alerts_ack_sequence_id) #ifdef ENABLE_NEW_CLOUD_PROTOCOL void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_ENTRY *ae, RRDHOST *host) { - char *edit_command = ae->source ? health_edit_command_from_source(ae->source) : strdupz("UNKNOWN=0"); + char *edit_command = ae->source ? health_edit_command_from_source(ae->source) : strdupz("UNKNOWN=0=UNKNOWN"); char config_hash_id[GUID_LEN + 1]; uuid_unparse_lower(ae->config_hash_id, config_hash_id); @@ -724,6 +744,7 @@ void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_EN } #endif +#ifdef ENABLE_NEW_CLOUD_PROTOCOL static int have_recent_alarm(RRDHOST *host, uint32_t alarm_id, time_t mark) { ALARM_ENTRY *ae = host->health_log.alarms; @@ -737,6 +758,7 @@ static int have_recent_alarm(RRDHOST *host, uint32_t alarm_id, time_t mark) return 0; } +#endif #define ALARM_EVENTS_PER_CHUNK 10 void aclk_push_alert_snapshot_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) diff --git a/database/sqlite/sqlite_aclk_chart.c b/database/sqlite/sqlite_aclk_chart.c index 4b887abaa..eea48a567 100644 --- a/database/sqlite/sqlite_aclk_chart.c +++ b/database/sqlite/sqlite_aclk_chart.c @@ -318,7 +318,7 @@ void aclk_send_chart_event(struct aclk_database_worker_config *wc, struct aclk_d uint64_t first_sequence; uint64_t last_sequence; - time_t last_timestamp; + time_t last_timestamp = 0; BUFFER *sql = buffer_create(1024); @@ -682,7 +682,9 @@ void aclk_start_streaming(char *node_id, uint64_t sequence_id, time_t created_at while(host) { if (host->node_id && !(uuid_compare(*host->node_id, node_uuid))) { rrd_unlock(); - wc = (struct aclk_database_worker_config *)host->dbsync_worker; + wc = (struct aclk_database_worker_config *)host->dbsync_worker ? + (struct aclk_database_worker_config *)host->dbsync_worker : + (struct aclk_database_worker_config *)find_inactive_wc_by_node_id(node_id); if (likely(wc)) { wc->chart_reset_count++; __sync_synchronize(); @@ -979,6 +981,8 @@ int queue_chart_to_aclk(RRDSET *st) #ifndef ENABLE_NEW_CLOUD_PROTOCOL #ifdef ENABLE_ACLK aclk_update_chart(st->rrdhost, st->id, 1); +#else + UNUSED(st); #endif return 0; #else diff --git a/database/sqlite/sqlite_aclk_node.c b/database/sqlite/sqlite_aclk_node.c index ba498c2a7..6261b9af5 100644 --- a/database/sqlite/sqlite_aclk_node.c +++ b/database/sqlite/sqlite_aclk_node.c @@ -22,6 +22,8 @@ void sql_build_node_info(struct aclk_database_worker_config *wc, struct aclk_dat node_info.claim_id = is_agent_claimed(); node_info.machine_guid = wc->host_guid; node_info.child = (wc->host != localhost); + node_info.ml_info.ml_capable = localhost->system_info->ml_capable; + node_info.ml_info.ml_enabled = wc->host->ml_host != NULL; now_realtime_timeval(&node_info.updated_at); RRDHOST *host = wc->host; @@ -46,6 +48,8 @@ void sql_build_node_info(struct aclk_database_worker_config *wc, struct aclk_dat node_info.data.services = NULL; // char ** node_info.data.service_count = 0; node_info.data.machine_guid = wc->host_guid; + node_info.data.ml_info.ml_capable = host->system_info->ml_capable; + node_info.data.ml_info.ml_enabled = host->system_info->ml_enabled; struct label_index *labels = &host->labels; netdata_rwlock_wrlock(&labels->labels_rwlock); -- cgit v1.2.3