| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #define RRDHOST_INTERNALS |
| 4 | #include "rrd.h" |
| 5 | #include "rrd-retention.h" |
| 6 | #include "libnetdata/parsers/duration.h" |
| 7 | |
| 8 | // Round retention time to more human-readable values (days/hours/minutes) |
| 9 | static time_t round_retention(time_t retention_seconds) { |
| 10 | if(retention_seconds > 60 * 86400) |
| 11 | retention_seconds = HOWMANY(retention_seconds, 86400) * 86400; |
| 12 | else if(retention_seconds > 86400) |
| 13 | retention_seconds = HOWMANY(retention_seconds, 3600) * 3600; |
| 14 | else |
| 15 | retention_seconds = HOWMANY(retention_seconds, 60) * 60; |
| 16 | |
| 17 | return retention_seconds; |
| 18 | } |
| 19 | |
| 20 | // Collect retention statistics from all tiers |
| 21 | RRDSTATS_RETENTION rrdstats_retention_collect(void) { |
| 22 | time_t now_s = now_realtime_sec(); |
| 23 | |
| 24 | // Initialize the retention structure |
| 25 | RRDSTATS_RETENTION retention = { |
| 26 | .storage_tiers = 0 |
| 27 | }; |
| 28 | |
| 29 | rrd_rdlock(); |
| 30 | |
| 31 | if(!localhost) { |
| 32 | rrd_rdunlock(); |
| 33 | return retention; |
| 34 | } |
| 35 | |
| 36 | // Count the available storage tiers |
| 37 | retention.storage_tiers = nd_profile.storage_tiers; |
| 38 | |
| 39 | // Iterate through all available storage tiers |
| 40 | for(size_t tier = 0; tier < retention.storage_tiers; tier++) { |
| 41 | STORAGE_ENGINE *eng = localhost->db[tier].eng; |
| 42 | if(!eng) |
| 43 | continue; |
| 44 | |
| 45 | RRD_STORAGE_TIER *tier_info = &retention.tiers[tier]; |
| 46 | tier_info->tier = tier; |
| 47 | tier_info->backend = eng->seb; |
| 48 | tier_info->group_seconds = get_tier_grouping(tier) * localhost->rrd_update_every; |
| 49 | |
| 50 | // Format human-readable granularity |
| 51 | duration_snprintf_time_t(tier_info->granularity_human, sizeof(tier_info->granularity_human), (time_t)tier_info->group_seconds); |
| 52 | |
| 53 | // Get metrics and samples counts |
| 54 | tier_info->metrics = storage_engine_metrics(eng->seb, localhost->db[tier].si); |
| 55 | tier_info->samples = storage_engine_samples(eng->seb, localhost->db[tier].si); |
| 56 | |
| 57 | // Get disk usage information |
| 58 | tier_info->disk_max = storage_engine_disk_space_max(eng->seb, localhost->db[tier].si); |
| 59 | tier_info->disk_used = storage_engine_disk_space_used(eng->seb, localhost->db[tier].si); |
| 60 | |
| 61 | #ifdef ENABLE_DBENGINE |
| 62 | if(!tier_info->disk_max && eng->seb == STORAGE_ENGINE_BACKEND_DBENGINE) { |
| 63 | tier_info->disk_max = rrdeng_get_directory_free_bytes_space(multidb_ctx[tier]); |
| 64 | tier_info->disk_max += tier_info->disk_used; |
| 65 | } |
| 66 | #endif |
| 67 | |
| 68 | // Calculate disk usage percentage |
| 69 | if(tier_info->disk_used && tier_info->disk_max) |
| 70 | tier_info->disk_percent = (double)tier_info->disk_used * 100.0 / (double)tier_info->disk_max; |
| 71 | else |
| 72 | tier_info->disk_percent = 0.0; |
| 73 | |
| 74 | // Get retention information |
| 75 | tier_info->first_time_s = storage_engine_global_first_time_s(eng->seb, localhost->db[tier].si); |
| 76 | tier_info->last_time_s = now_s; |
| 77 | |
| 78 | if(tier_info->first_time_s < tier_info->last_time_s) { |
| 79 | tier_info->retention = tier_info->last_time_s - tier_info->first_time_s; |
| 80 | |
| 81 | // Format human-readable retention |
| 82 | duration_snprintf(tier_info->retention_human, sizeof(tier_info->retention_human), |
| 83 | round_retention(tier_info->retention), "s", false); |
| 84 | |
| 85 | if(tier_info->disk_used || tier_info->disk_max) { |
| 86 | // Get requested retention time |
| 87 | tier_info->requested_retention = 0; |
| 88 | #ifdef ENABLE_DBENGINE |
| 89 | if(eng->seb == STORAGE_ENGINE_BACKEND_DBENGINE) |
| 90 | tier_info->requested_retention = multidb_ctx[tier]->config.max_retention_s; |
| 91 | #endif |
| 92 | |
| 93 | // Format human-readable requested retention |
| 94 | duration_snprintf(tier_info->requested_retention_human, sizeof(tier_info->requested_retention_human), |
| 95 | (int)tier_info->requested_retention, "s", false); |
| 96 | |
| 97 | // Calculate expected retention based on current usage |
| 98 | time_t space_retention = 0; |
| 99 | if(tier_info->disk_percent > 0) |
| 100 | space_retention = (time_t)((double)(now_s - tier_info->first_time_s) * 100.0 / tier_info->disk_percent); |
| 101 | |
| 102 | tier_info->expected_retention = (tier_info->requested_retention && tier_info->requested_retention < space_retention) |
| 103 | ? tier_info->requested_retention |
| 104 | : space_retention; |
| 105 | |
| 106 | // Format human-readable expected retention |
| 107 | duration_snprintf(tier_info->expected_retention_human, sizeof(tier_info->expected_retention_human), |
| 108 | (int)round_retention(tier_info->expected_retention), "s", false); |
| 109 | } |
| 110 | } |
| 111 | else { |
| 112 | // No data yet in this tier |
| 113 | tier_info->retention = 0; |
| 114 | tier_info->retention_human[0] = '\0'; |
| 115 | tier_info->requested_retention = 0; |
| 116 | tier_info->requested_retention_human[0] = '\0'; |
| 117 | tier_info->expected_retention = 0; |
| 118 | tier_info->expected_retention_human[0] = '\0'; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | rrd_rdunlock(); |
| 123 | |
| 124 | return retention; |
| 125 | } |