summaryrefslogtreecommitdiffstats
path: root/database/rrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'database/rrd.c')
-rw-r--r--database/rrd.c441
1 files changed, 354 insertions, 87 deletions
diff --git a/database/rrd.c b/database/rrd.c
index 5b7752a5ea..fd22c25766 100644
--- a/database/rrd.c
+++ b/database/rrd.c
@@ -1,70 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#define NETDATA_RRD_INTERNALS 1
#include "rrd.h"
-#include "storage_engine.h"
-
-// ----------------------------------------------------------------------------
-// globals
-
-/*
-// if not zero it gives the time (in seconds) to remove un-updated dimensions
-// DO NOT ENABLE
-// if dimensions are removed, the chart generation will have to run again
-int rrd_delete_unupdated_dimensions = 0;
-*/
-
-int default_rrd_update_every = UPDATE_EVERY;
-int default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
-#ifdef ENABLE_DBENGINE
-RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE;
-#else
-RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
-#endif
-int gap_when_lost_iterations_above = 1;
-
-
-// ----------------------------------------------------------------------------
-// RRD - memory modes
-
-inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) {
- switch(id) {
- case RRD_MEMORY_MODE_RAM:
- return RRD_MEMORY_MODE_RAM_NAME;
-
- case RRD_MEMORY_MODE_MAP:
- return RRD_MEMORY_MODE_MAP_NAME;
-
- case RRD_MEMORY_MODE_NONE:
- return RRD_MEMORY_MODE_NONE_NAME;
-
- case RRD_MEMORY_MODE_SAVE:
- return RRD_MEMORY_MODE_SAVE_NAME;
-
- case RRD_MEMORY_MODE_ALLOC:
- return RRD_MEMORY_MODE_ALLOC_NAME;
-
- case RRD_MEMORY_MODE_DBENGINE:
- return RRD_MEMORY_MODE_DBENGINE_NAME;
- }
-
- STORAGE_ENGINE* eng = storage_engine_get(id);
- if (eng) {
- return eng->name;
- }
-
- return RRD_MEMORY_MODE_SAVE_NAME;
-}
-
-RRD_MEMORY_MODE rrd_memory_mode_id(const char *name) {
- STORAGE_ENGINE* eng = storage_engine_find(name);
- if (eng) {
- return eng->id;
- }
-
- return RRD_MEMORY_MODE_SAVE;
-}
-
// ----------------------------------------------------------------------------
// RRD - algorithms types
@@ -107,7 +43,7 @@ const char *rrd_algorithm_name(RRD_ALGORITHM algorithm) {
// ----------------------------------------------------------------------------
// RRD - chart types
-inline RRDSET_TYPE rrdset_type_id(const char *name) {
+RRDSET_TYPE rrdset_type_id(const char *name) {
if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0))
return RRDSET_TYPE_AREA;
@@ -133,28 +69,6 @@ const char *rrdset_type_name(RRDSET_TYPE chart_type) {
}
// ----------------------------------------------------------------------------
-// RRD - cache directory
-
-char *rrdhost_cache_dir_for_rrdset_alloc(RRDHOST *host, const char *id) {
- char *ret = NULL;
-
- char b[FILENAME_MAX + 1];
- char n[FILENAME_MAX + 1];
- rrdset_strncpyz_name(b, id, FILENAME_MAX);
-
- snprintfz(n, FILENAME_MAX, "%s/%s", host->cache_dir, b);
- ret = strdupz(n);
-
- if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
- int r = mkdir(ret, 0775);
- if(r != 0 && errno != EEXIST)
- netdata_log_error("Cannot create directory '%s'", ret);
- }
-
- return ret;
-}
-
-// ----------------------------------------------------------------------------
// RRD - string management
STRING *rrd_string_strdupz(const char *s) {
@@ -166,3 +80,356 @@ STRING *rrd_string_strdupz(const char *s) {
freez(tmp);
return ret;
}
+
+// ----------------------------------------------------------------------------
+// rrd global / startup initialization
+
+#ifdef ENABLE_DBENGINE
+struct dbengine_initialization {
+ netdata_thread_t thread;
+ char path[FILENAME_MAX + 1];
+ int disk_space_mb;
+ size_t tier;
+ int ret;
+};
+
+static void *dbengine_tier_init(void *ptr) {
+ struct dbengine_initialization *dbi = ptr;
+ dbi->ret = rrdeng_init(NULL, dbi->path, dbi->disk_space_mb, dbi->tier);
+ return ptr;
+}
+#endif
+
+static void dbengine_init(const char *hostname) {
+#ifdef ENABLE_DBENGINE
+ rrdb.use_direct_io = config_get_boolean(CONFIG_SECTION_DB, "dbengine use direct io", rrdb.use_direct_io);
+
+ unsigned read_num = (unsigned)config_get_number(CONFIG_SECTION_DB, "dbengine pages per extent", MAX_PAGES_PER_EXTENT);
+ if (read_num > 0 && read_num <= MAX_PAGES_PER_EXTENT)
+ rrdeng_pages_per_extent = read_num;
+ else {
+ netdata_log_error("Invalid dbengine pages per extent %u given. Using %u.", read_num, rrdeng_pages_per_extent);
+ config_set_number(CONFIG_SECTION_DB, "dbengine pages per extent", rrdeng_pages_per_extent);
+ }
+
+ rrdb.storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", rrdb.storage_tiers);
+ if (rrdb.storage_tiers < 1) {
+ netdata_log_error("At least 1 storage tier is required. Assuming 1.");
+ rrdb.storage_tiers = 1;
+ config_set_number(CONFIG_SECTION_DB, "storage tiers", rrdb.storage_tiers);
+ }
+ if (rrdb.storage_tiers > RRD_STORAGE_TIERS) {
+ netdata_log_error("Up to %d storage tier are supported. Assuming %d.", RRD_STORAGE_TIERS, RRD_STORAGE_TIERS);
+ rrdb.storage_tiers = RRD_STORAGE_TIERS;
+ config_set_number(CONFIG_SECTION_DB, "storage tiers", rrdb.storage_tiers);
+ }
+
+ bool parallel_initialization = (rrdb.storage_tiers <= (size_t)get_netdata_cpus()) ? true : false;
+ parallel_initialization = config_get_boolean(CONFIG_SECTION_DB, "dbengine parallel initialization", parallel_initialization);
+
+ struct dbengine_initialization tiers_init[RRD_STORAGE_TIERS] = {};
+
+ size_t created_tiers = 0;
+ char dbenginepath[FILENAME_MAX + 1];
+ char dbengineconfig[200 + 1];
+ int divisor = 1;
+ for(size_t tier = 0; tier < rrdb.storage_tiers ;tier++) {
+ if(tier == 0)
+ snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine", netdata_configured_cache_dir);
+ else
+ snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine-tier%zu", netdata_configured_cache_dir, tier);
+
+ int ret = mkdir(dbenginepath, 0775);
+ if (ret != 0 && errno != EEXIST) {
+ netdata_log_error("DBENGINE on '%s': cannot create directory '%s'", hostname, dbenginepath);
+ break;
+ }
+
+ if(tier > 0)
+ divisor *= 2;
+
+ int disk_space_mb = rrdb.default_multidb_disk_quota_mb / divisor;
+ size_t grouping_iterations = rrdb.storage_tiers_grouping_iterations[tier];
+ RRD_BACKFILL backfill = rrdb.storage_tiers_backfill[tier];
+
+ if(tier > 0) {
+ snprintfz(dbengineconfig, 200, "dbengine tier %zu multihost disk space MB", tier);
+ disk_space_mb = config_get_number(CONFIG_SECTION_DB, dbengineconfig, disk_space_mb);
+
+ snprintfz(dbengineconfig, 200, "dbengine tier %zu update every iterations", tier);
+ grouping_iterations = config_get_number(CONFIG_SECTION_DB, dbengineconfig, grouping_iterations);
+ if(grouping_iterations < 2) {
+ grouping_iterations = 2;
+ config_set_number(CONFIG_SECTION_DB, dbengineconfig, grouping_iterations);
+ netdata_log_error("DBENGINE on '%s': 'dbegnine tier %zu update every iterations' cannot be less than 2. Assuming 2.",
+ hostname,
+ tier);
+ }
+
+ snprintfz(dbengineconfig, 200, "dbengine tier %zu backfill", tier);
+ const char *bf = config_get(CONFIG_SECTION_DB, dbengineconfig, backfill == RRD_BACKFILL_NEW ? "new" : backfill == RRD_BACKFILL_FULL ? "full" : "none");
+ if(strcmp(bf, "new") == 0) backfill = RRD_BACKFILL_NEW;
+ else if(strcmp(bf, "full") == 0) backfill = RRD_BACKFILL_FULL;
+ else if(strcmp(bf, "none") == 0) backfill = RRD_BACKFILL_NONE;
+ else {
+ netdata_log_error("DBENGINE: unknown backfill value '%s', assuming 'new'", bf);
+ config_set(CONFIG_SECTION_DB, dbengineconfig, "new");
+ backfill = RRD_BACKFILL_NEW;
+ }
+ }
+
+ rrdb.storage_tiers_grouping_iterations[tier] = grouping_iterations;
+ rrdb.storage_tiers_backfill[tier] = backfill;
+
+ if(tier > 0 && get_tier_grouping(tier) > 65535) {
+ rrdb.storage_tiers_grouping_iterations[tier] = 1;
+ netdata_log_error("DBENGINE on '%s': dbengine tier %zu gives aggregation of more than 65535 points of tier 0. Disabling tiers above %zu",
+ hostname,
+ tier,
+ tier);
+ break;
+ }
+
+ internal_error(true, "DBENGINE tier %zu grouping iterations is set to %zu", tier, rrdb.storage_tiers_grouping_iterations[tier]);
+
+ tiers_init[tier].disk_space_mb = disk_space_mb;
+ tiers_init[tier].tier = tier;
+ strncpyz(tiers_init[tier].path, dbenginepath, FILENAME_MAX);
+ tiers_init[tier].ret = 0;
+
+ if(parallel_initialization) {
+ char tag[NETDATA_THREAD_TAG_MAX + 1];
+ snprintfz(tag, NETDATA_THREAD_TAG_MAX, "DBENGINIT[%zu]", tier);
+ netdata_thread_create(&tiers_init[tier].thread, tag, NETDATA_THREAD_OPTION_JOINABLE,
+ dbengine_tier_init, &tiers_init[tier]);
+ }
+ else
+ dbengine_tier_init(&tiers_init[tier]);
+ }
+
+ for (size_t tier = 0; tier < rrdb.storage_tiers ;tier++) {
+ void *ptr;
+
+ if(parallel_initialization)
+ netdata_thread_join(tiers_init[tier].thread, &ptr);
+
+ if(tiers_init[tier].ret != 0) {
+ netdata_log_error("DBENGINE on '%s': Failed to initialize multi-host database tier %zu on path '%s'",
+ hostname,
+ tiers_init[tier].tier,
+ tiers_init[tier].path);
+ }
+ else if(created_tiers == tier)
+ created_tiers++;
+ }
+
+ if(created_tiers && created_tiers < rrdb.storage_tiers) {
+ netdata_log_error("DBENGINE on '%s': Managed to create %zu tiers instead of %zu. Continuing with %zu available.",
+ hostname,
+ created_tiers,
+ rrdb.storage_tiers,
+ created_tiers);
+ rrdb.storage_tiers = created_tiers;
+ }
+ else if(!created_tiers)
+ fatal("DBENGINE on '%s', failed to initialize databases at '%s'.", hostname, netdata_configured_cache_dir);
+
+ for(size_t tier = 0; tier < rrdb.storage_tiers ;tier++)
+ rrdeng_readiness_wait(rrdb.multidb_ctx[tier]);
+
+ rrdb.dbengine_enabled = true;
+#else
+ rrdb.storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", 1);
+ if(rrdb.storage_tiers != 1) {
+ netdata_log_error("DBENGINE is not available on '%s', so only 1 database tier can be supported.", hostname);
+ rrdb.storage_tiers = 1;
+ config_set_number(CONFIG_SECTION_DB, "storage tiers", rrdb.storage_tiers);
+ }
+ rrdb.dbengine_enabled = false;
+#endif
+}
+
+static void init_host_indexes() {
+ internal_fatal(rrdb.rrdhost_root_index || rrdb.rrdhost_root_index_hostname,
+ "Host indexes have already been initialized");
+
+ DICT_OPTIONS dict_opts = DICT_OPTION_NAME_LINK_DONT_CLONE |
+ DICT_OPTION_VALUE_LINK_DONT_CLONE |
+ DICT_OPTION_DONT_OVERWRITE_VALUE;
+
+ rrdb.rrdhost_root_index = dictionary_create_advanced(dict_opts, &dictionary_stats_category_rrdhost, 0);
+ rrdb.rrdhost_root_index_hostname = dictionary_create_advanced(dict_opts, &dictionary_stats_category_rrdhost, 0);
+}
+
+int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unittest) {
+ init_host_indexes();
+
+ if (unlikely(sql_init_database(DB_CHECK_NONE, system_info ? 0 : 1))) {
+ if (default_storage_engine_id == STORAGE_ENGINE_DBENGINE) {
+ set_late_global_environment(system_info);
+ fatal("Failed to initialize SQLite");
+ }
+ netdata_log_info("Skipping SQLITE metadata initialization since memory mode is not dbengine");
+ }
+
+ if (unlikely(sql_init_context_database(system_info ? 0 : 1))) {
+ error_report("Failed to initialize context metadata database");
+ }
+
+ if (unlikely(unittest)) {
+ rrdb.dbengine_enabled = true;
+ }
+ else {
+ health_init();
+ rrdpush_init();
+
+ if (default_storage_engine_id == STORAGE_ENGINE_DBENGINE || rrdpush_receiver_needs_dbengine()) {
+ netdata_log_info("DBENGINE: Initializing ...");
+ dbengine_init(hostname);
+ }
+ else {
+ netdata_log_info("DBENGINE: Not initializing ...");
+ rrdb.storage_tiers = 1;
+ }
+
+ if (!rrdb.dbengine_enabled) {
+ if (rrdb.storage_tiers > 1) {
+ netdata_log_error("dbengine is not enabled, but %zu tiers have been requested. Resetting tiers to 1",
+ rrdb.storage_tiers);
+ rrdb.storage_tiers = 1;
+ }
+
+ if (default_storage_engine_id == STORAGE_ENGINE_DBENGINE) {
+ netdata_log_error("dbengine is not enabled, but it has been given as the default db mode. Resetting db mode to alloc");
+ default_storage_engine_id = STORAGE_ENGINE_ALLOC;
+ }
+ }
+ }
+
+ if(!unittest)
+ metadata_sync_init();
+
+ netdata_log_debug(D_RRDHOST, "Initializing localhost with hostname '%s'", hostname);
+ rrdb.localhost = rrdhost_create(
+ hostname
+ , registry_get_this_machine_hostname()
+ , registry_get_this_machine_guid()
+ , os_type
+ , netdata_configured_timezone
+ , netdata_configured_abbrev_timezone
+ , netdata_configured_utc_offset
+ , ""
+ , program_name
+ , program_version
+ , rrdb.default_update_every
+ , rrdb.default_rrd_history_entries
+ , default_storage_engine_id
+ , default_health_enabled
+ , default_rrdpush_enabled
+ , default_rrdpush_destination
+ , default_rrdpush_api_key
+ , default_rrdpush_send_charts_matching
+ , default_rrdpush_enable_replication
+ , default_rrdpush_seconds_to_replicate
+ , default_rrdpush_replication_step
+ , system_info
+ , 1
+ , 0
+ );
+
+ if (unlikely(!rrdb.localhost)) {
+ return 1;
+ }
+
+#if NETDATA_DEV_MODE
+ // we register this only on localhost
+ // for the other nodes, the origin server should register it
+ rrd_collector_started(); // this creates a collector that runs for as long as netdata runs
+ rrd_collector_add_function(rrdb.localhost, NULL, "streaming", 10,
+ RRDFUNCTIONS_STREAMING_HELP, true,
+ rrdhost_function_streaming, NULL);
+#endif
+
+ if (likely(system_info)) {
+ migrate_localhost(&rrdb.localhost->host_uuid);
+ sql_aclk_sync_init();
+ web_client_api_v1_management_init();
+ }
+ return rrdb.localhost == NULL;
+}
+
+struct rrdb rrdb = {
+ .rrdhost_root_index = NULL,
+ .rrdhost_root_index_hostname = NULL,
+ .unittest_running = false,
+ .dbengine_enabled = false,
+ .storage_tiers = 3,
+ .use_direct_io = true,
+ .storage_tiers_grouping_iterations = {
+ 1,
+ 60,
+ 60,
+ 60,
+ 60
+ },
+ .storage_tiers_backfill = {
+ RRD_BACKFILL_NEW,
+ RRD_BACKFILL_NEW,
+ RRD_BACKFILL_NEW,
+ RRD_BACKFILL_NEW,
+ RRD_BACKFILL_NEW
+ },
+ .default_update_every = UPDATE_EVERY_MIN,
+ .default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES,
+ .gap_when_lost_iterations_above = 1,
+ .rrdset_free_obsolete_time_s = RRD_DEFAULT_HISTORY_ENTRIES,
+ .libuv_worker_threads = 8,
+ .ieee754_doubles = false,
+ .rrdhost_free_orphan_time_s = RRD_DEFAULT_HISTORY_ENTRIES,
+ .rrd_rwlock = NETDATA_RWLOCK_INITIALIZER,
+ .localhost = NULL,
+
+#if defined(ENV32BIT)
+ .default_rrdeng_page_cache_mb = 16,
+ .default_rrdeng_extent_cache_mb = 0,
+#else
+ .default_rrdeng_page_cache_mb = 32,
+ .default_rrdeng_extent_cache_mb = 0,
+#endif
+
+ .db_engine_journal_check = CONFIG_BOOLEAN_NO,
+
+ .default_rrdeng_disk_quota_mb = 256,
+ .default_multidb_disk_quota_mb = 256,
+
+ .multidb_ctx = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+
+ .page_type_size = {
+ sizeof(storage_number),
+ sizeof(storage_number_tier1_t)
+ },
+
+#if defined(ENV32BIT)
+ .tier_page_size = {
+ 2048,
+ 1024,
+ 192,
+ 192,
+ 192
+ },
+#else
+ .tier_page_size = {
+ 4096,
+ 2048,
+ 384,
+ 384,
+ 384
+ },
+#endif
+};