| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "rrdset.h" |
| 4 | #include "storage-engine.h" |
| 5 | |
| 6 | void rrdset_metadata_updated(RRDSET *st) { |
| 7 | __atomic_add_fetch(&st->version, 1, __ATOMIC_RELAXED); |
| 8 | rrdcontext_updated_rrdset(st); |
| 9 | } |
| 10 | |
| 11 | // ---------------------------------------------------------------------------- |
| 12 | |
| 13 | // get the timestamp of the last entry in the round-robin database |
| 14 | time_t rrdset_last_entry_s(RRDSET *st) { |
| 15 | RRDDIM *rd; |
| 16 | time_t last_entry_s = 0; |
| 17 | |
| 18 | rrddim_foreach_read(rd, st) { |
| 19 | time_t t = rrddim_last_entry_s(rd); |
| 20 | if(t > last_entry_s) last_entry_s = t; |
| 21 | } |
| 22 | rrddim_foreach_done(rd); |
| 23 | |
| 24 | return last_entry_s; |
| 25 | } |
| 26 | |
| 27 | time_t rrdset_last_entry_s_of_tier(RRDSET *st, size_t tier) { |
| 28 | RRDDIM *rd; |
| 29 | time_t last_entry_s = 0; |
| 30 | |
| 31 | rrddim_foreach_read(rd, st) { |
| 32 | time_t t = rrddim_last_entry_s_of_tier(rd, tier); |
| 33 | if(t > last_entry_s) last_entry_s = t; |
| 34 | } |
| 35 | rrddim_foreach_done(rd); |
| 36 | |
| 37 | return last_entry_s; |
| 38 | } |
| 39 | |
| 40 | // get the timestamp of first entry in the round-robin database |
| 41 | time_t rrdset_first_entry_s(RRDSET *st) { |
| 42 | RRDDIM *rd; |
| 43 | time_t first_entry_s = LONG_MAX; |
| 44 | |
| 45 | rrddim_foreach_read(rd, st) { |
| 46 | time_t t = rrddim_first_entry_s(rd); |
| 47 | if(t < first_entry_s) |
| 48 | first_entry_s = t; |
| 49 | } |
| 50 | rrddim_foreach_done(rd); |
| 51 | |
| 52 | if (unlikely(LONG_MAX == first_entry_s)) return 0; |
| 53 | return first_entry_s; |
| 54 | } |
| 55 | |
| 56 | time_t rrdset_first_entry_s_of_tier(RRDSET *st, size_t tier) { |
| 57 | if(unlikely(tier >= nd_profile.storage_tiers)) |
| 58 | return 0; |
| 59 | |
| 60 | RRDDIM *rd; |
| 61 | time_t first_entry_s = LONG_MAX; |
| 62 | |
| 63 | rrddim_foreach_read(rd, st) { |
| 64 | time_t t = rrddim_first_entry_s_of_tier(rd, tier); |
| 65 | if(t && t < first_entry_s) |
| 66 | first_entry_s = t; |
| 67 | } |
| 68 | rrddim_foreach_done(rd); |
| 69 | |
| 70 | if (unlikely(LONG_MAX == first_entry_s)) return 0; |
| 71 | return first_entry_s; |
| 72 | } |
| 73 | |
| 74 | void rrdset_get_retention_of_tier_for_collected_chart(RRDSET *st, time_t *first_time_s, time_t *last_time_s, time_t now_s, size_t tier) { |
| 75 | if(!now_s) |
| 76 | now_s = now_realtime_sec(); |
| 77 | |
| 78 | time_t db_first_entry_s = rrdset_first_entry_s_of_tier(st, tier); |
| 79 | time_t db_last_entry_s = st->last_updated.tv_sec; // we assume this is a collected RRDSET |
| 80 | |
| 81 | if(unlikely(!db_last_entry_s)) { |
| 82 | db_last_entry_s = rrdset_last_entry_s_of_tier(st, tier); |
| 83 | |
| 84 | if (unlikely(!db_last_entry_s)) { |
| 85 | // we assume this is a collected RRDSET |
| 86 | db_first_entry_s = 0; |
| 87 | db_last_entry_s = 0; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | if(unlikely(db_last_entry_s > now_s)) { |
| 92 | internal_error(db_last_entry_s > now_s + 1, |
| 93 | "RRDSET: 'host:%s/chart:%s' latest db time %ld is in the future, adjusting it to now %ld", |
| 94 | rrdhost_hostname(st->rrdhost), rrdset_id(st), |
| 95 | db_last_entry_s, now_s); |
| 96 | db_last_entry_s = now_s; |
| 97 | } |
| 98 | |
| 99 | if(unlikely(db_first_entry_s && db_last_entry_s && db_first_entry_s >= db_last_entry_s)) { |
| 100 | internal_error(db_first_entry_s > db_last_entry_s, |
| 101 | "RRDSET: 'host:%s/chart:%s' oldest db time %ld is bigger than latest db time %ld, adjusting it to (latest time %ld - update every %ld)", |
| 102 | rrdhost_hostname(st->rrdhost), rrdset_id(st), |
| 103 | db_first_entry_s, db_last_entry_s, |
| 104 | db_last_entry_s, (time_t)st->update_every); |
| 105 | db_first_entry_s = db_last_entry_s - st->update_every; |
| 106 | } |
| 107 | |
| 108 | if(unlikely(!db_first_entry_s && db_last_entry_s)) |
| 109 | // this can be the case on the first data collection of a chart |
| 110 | db_first_entry_s = db_last_entry_s - st->update_every; |
| 111 | |
| 112 | *first_time_s = db_first_entry_s; |
| 113 | *last_time_s = db_last_entry_s; |
| 114 | } |
| 115 | |
| 116 | void rrdset_is_obsolete___safe_from_collector_thread(RRDSET *st) { |
| 117 | if(!st) return; |
| 118 | |
| 119 | rrdset_pluginsd_receive_unslot(st); |
| 120 | |
| 121 | if(unlikely(!(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))) { |
| 122 | // netdata_log_info("Setting obsolete flag on chart 'host:%s/chart:%s'", |
| 123 | // rrdhost_hostname(st->rrdhost), rrdset_id(st)); |
| 124 | |
| 125 | rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE); |
| 126 | rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_OBSOLETE_CHARTS); |
| 127 | |
| 128 | st->last_accessed_time_s = now_realtime_sec(); |
| 129 | |
| 130 | // The parent skips replication for obsolete charts, so the natural |
| 131 | // "replication finished" decrement at stream-replication-sender.c will |
| 132 | // never fire for this chart. Release any pending replication slot now, |
| 133 | // otherwise rrdhost_sender_replicating_charts pins above zero and |
| 134 | // observability (SND_REPLICATING vs SND_RUNNING) stays stuck on the |
| 135 | // wrong state. Mirror the natural-finalize path's pulse-status flip on |
| 136 | // the 0/1 boundary so SND_REPLICATING -> SND_RUNNING is observed. |
| 137 | RRDSET_FLAGS old_repl = rrdset_flag_set_and_clear( |
| 138 | st, |
| 139 | RRDSET_FLAG_SENDER_REPLICATION_FINISHED, |
| 140 | RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS); |
| 141 | if(old_repl & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS) { |
| 142 | if(rrdhost_sender_replicating_charts_minus_one(st->rrdhost) == 0) |
| 143 | pulse_host_status(st->rrdhost, PULSE_HOST_STATUS_SND_RUNNING, 0); |
| 144 | } |
| 145 | |
| 146 | rrdset_metadata_updated(st); |
| 147 | |
| 148 | // the chart will not get more updates (data collection) |
| 149 | // so, we have to push its definition now |
| 150 | stream_sender_send_rrdset_definition_now(st); |
| 151 | rrdcontext_updated_rrdset_flags(st); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | void rrdset_isnot_obsolete___safe_from_collector_thread(RRDSET *st) { |
| 156 | if(unlikely((rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))) { |
| 157 | |
| 158 | // netdata_log_info("Clearing obsolete flag on chart 'host:%s/chart:%s'", |
| 159 | // rrdhost_hostname(st->rrdhost), rrdset_id(st)); |
| 160 | |
| 161 | rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); |
| 162 | st->last_accessed_time_s = now_realtime_sec(); |
| 163 | |
| 164 | rrdset_metadata_updated(st); |
| 165 | |
| 166 | // the chart will be pushed upstream automatically |
| 167 | // due to data collection |
| 168 | rrdcontext_updated_rrdset_flags(st); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | void rrdset_update_heterogeneous_flag(RRDSET *st) { |
| 173 | RRDHOST *host = st->rrdhost; |
| 174 | (void)host; |
| 175 | |
| 176 | RRDDIM *rd; |
| 177 | |
| 178 | rrdset_flag_clear(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); |
| 179 | |
| 180 | bool init = false, is_heterogeneous = false; |
| 181 | RRD_ALGORITHM algorithm; |
| 182 | int32_t multiplier; |
| 183 | int32_t divisor; |
| 184 | |
| 185 | rrddim_foreach_read(rd, st) { |
| 186 | if(!init) { |
| 187 | algorithm = rd->algorithm; |
| 188 | multiplier = rd->multiplier; |
| 189 | divisor = ABS(rd->divisor); |
| 190 | init = true; |
| 191 | continue; |
| 192 | } |
| 193 | |
| 194 | if(algorithm != rd->algorithm || multiplier != ABS(rd->multiplier) || divisor != ABS(rd->divisor)) { |
| 195 | if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) { |
| 196 | #ifdef NETDATA_INTERNAL_CHECKS |
| 197 | netdata_log_info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already present " |
| 198 | "(algorithm is '%s' vs '%s', multiplier is %d vs %d, " |
| 199 | "divisor is %d vs %d).", |
| 200 | rrddim_name(rd), |
| 201 | rrdset_name(st), |
| 202 | rrdhost_hostname(host), |
| 203 | rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm), |
| 204 | rd->multiplier, multiplier, |
| 205 | rd->divisor, divisor |
| 206 | ); |
| 207 | #endif |
| 208 | rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS); |
| 209 | } |
| 210 | |
| 211 | is_heterogeneous = true; |
| 212 | break; |
| 213 | } |
| 214 | } |
| 215 | rrddim_foreach_done(rd); |
| 216 | |
| 217 | if(!is_heterogeneous) { |
| 218 | rrdset_flag_clear(st, RRDSET_FLAG_HETEROGENEOUS); |
| 219 | rrdcontext_updated_rrdset_flags(st); |
| 220 | } |
| 221 | } |