summaryrefslogtreecommitdiffstats
path: root/database
diff options
context:
space:
mode:
Diffstat (limited to 'database')
-rw-r--r--database/engine/README.md6
-rw-r--r--database/engine/pagecache.c6
-rw-r--r--database/engine/rrdengine.c18
-rwxr-xr-xdatabase/engine/rrdengineapi.c12
-rw-r--r--database/engine/rrdenginelib.h40
-rw-r--r--database/rrd.h14
-rw-r--r--database/rrdhost.c17
-rw-r--r--database/rrdset.c9
-rw-r--r--database/sqlite/sqlite3.c2
-rw-r--r--database/sqlite/sqlite_aclk.c18
-rw-r--r--database/sqlite/sqlite_aclk.h1
-rw-r--r--database/sqlite/sqlite_aclk_alert.c30
-rw-r--r--database/sqlite/sqlite_aclk_chart.c8
-rw-r--r--database/sqlite/sqlite_aclk_node.c4
14 files changed, 110 insertions, 75 deletions
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);