master
c 443 lines 14.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "exporting_engine.h"
4
5 /**
6 * Normalize chart and dimension names
7 *
8 * Substitute '_' for any special character except '.'.
9 *
10 * @param dst where to copy name to.
11 * @param src where to copy name from.
12 * @param max_len the maximum size of copied name.
13 * @return Returns the size of the copied name.
14 */
15 size_t exporting_name_copy(char *dst, const char *src, size_t max_len)
16 {
17 size_t n;
18
19 for (n = 0; *src && n < max_len; dst++, src++, n++) {
20 char c = *src;
21
22 if (c != '.' && !isalnum(c))
23 *dst = '_';
24 else
25 *dst = c;
26 }
27 *dst = '\0';
28
29 return n;
30 }
31
32 /**
33 * Mark scheduled instances
34 *
35 * Any instance can have its own update interval. On every exporting engine update only those instances are picked,
36 * which are scheduled for the update.
37 *
38 * @param engine an engine data structure.
39 * @return Returns 1 if there are instances to process
40 */
41 int mark_scheduled_instances(struct engine *engine)
42 {
43 int instances_were_scheduled = 0;
44
45 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
46 if (!instance->disabled && (engine->now % instance->config.update_every >=
47 instance->config.update_every - localhost->rrd_update_every)) {
48 instance->scheduled = 1;
49 instances_were_scheduled = 1;
50 instance->before = engine->now;
51 }
52 }
53
54 return instances_were_scheduled;
55 }
56
57 /**
58 * Calculate the SUM or AVERAGE of a dimension, for any timeframe
59 *
60 * May return NAN if the database does not have any value in the give timeframe.
61 *
62 * @param instance an instance data structure.
63 * @param rd a dimension(metric) in the Netdata database.
64 * @param last_timestamp the timestamp that should be reported to the exporting connector instance.
65 * @return Returns the value, calculated over the given period.
66 */
67 NETDATA_DOUBLE exporting_calculate_value_from_stored_data(
68 struct instance *instance,
69 RRDDIM *rd,
70 time_t *last_timestamp)
71 {
72 RRDSET *st = rd->rrdset;
73 #ifdef NETDATA_INTERNAL_CHECKS
74 RRDHOST *host = st->rrdhost;
75 #endif
76 time_t after = instance->after;
77 time_t before = instance->before;
78
79 // find the edges of the rrd database for this chart
80 time_t first_t = storage_engine_oldest_time_s(rd->tiers[0].seb, rd->tiers[0].smh);
81 time_t last_t = storage_engine_latest_time_s(rd->tiers[0].seb, rd->tiers[0].smh);
82 time_t update_every = st->update_every;
83 struct storage_engine_query_handle handle;
84
85 // step back a little, to make sure we have complete data collection
86 // for all metrics
87 after -= update_every * 2;
88 before -= update_every * 2;
89
90 // align the time-frame
91 after = after - (after % update_every);
92 before = before - (before % update_every);
93
94 // for before, loose another iteration
95 // the latest point will be reported the next time
96 before -= update_every;
97
98 if (unlikely(after > before))
99 // this can happen when update_every > before - after
100 after = before;
101
102 if (unlikely(after < first_t))
103 after = first_t;
104
105 if (unlikely(before > last_t))
106 before = last_t;
107
108 if (unlikely(before < first_t || after > last_t)) {
109 // the chart has not been updated in the wanted timeframe
110 netdata_log_debug(
111 D_EXPORTING,
112 "EXPORTING: %s.%s.%s: aligned timeframe %lu to %lu is outside the chart's database range %lu to %lu",
113 rrdhost_hostname(host),
114 rrdset_id(st),
115 rrddim_id(rd),
116 (unsigned long)after,
117 (unsigned long)before,
118 (unsigned long)first_t,
119 (unsigned long)last_t);
120 return NAN;
121 }
122
123 *last_timestamp = before;
124
125 size_t points_read = 0;
126 size_t counter = 0;
127 NETDATA_DOUBLE sum = 0;
128
129 for (storage_engine_query_init(rd->tiers[0].seb, rd->tiers[0].smh, &handle, after, before, STORAGE_PRIORITY_SYNCHRONOUS); !storage_engine_query_is_finished(&handle);) {
130 STORAGE_POINT sp = storage_engine_query_next_metric(&handle);
131 points_read++;
132
133 if (unlikely(storage_point_is_gap(sp))) {
134 // not collected
135 continue;
136 }
137
138 sum += sp.sum;
139 counter += sp.count;
140 }
141 storage_engine_query_finalize(&handle);
142 pulse_queries_exporters_query_completed(points_read);
143
144 if (unlikely(!counter)) {
145 netdata_log_debug(
146 D_EXPORTING,
147 "EXPORTING: %s.%s.%s: no values stored in database for range %lu to %lu",
148 rrdhost_hostname(host),
149 rrdset_id(st),
150 rrddim_id(rd),
151 (unsigned long)after,
152 (unsigned long)before);
153 return NAN;
154 }
155
156 if (unlikely(EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_SUM))
157 return sum;
158
159 return sum / (NETDATA_DOUBLE)counter;
160 }
161
162 /**
163 * Start batch formatting for every connector instance's buffer
164 *
165 * @param engine an engine data structure.
166 */
167 void start_batch_formatting(struct engine *engine)
168 {
169 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
170 if (instance->scheduled) {
171 netdata_mutex_lock(&instance->mutex);
172 if (instance->start_batch_formatting && instance->start_batch_formatting(instance) != 0) {
173 netdata_log_error("EXPORTING: cannot start batch formatting for %s", instance->config.name);
174 disable_instance(instance);
175 }
176 }
177 }
178 }
179
180 /**
181 * Start host formatting for every connector instance's buffer
182 *
183 * @param engine an engine data structure.
184 * @param host a data collecting host.
185 */
186 void start_host_formatting(struct engine *engine, RRDHOST *host)
187 {
188 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
189 if (instance->scheduled) {
190 if (rrdhost_is_exportable(instance, host)) {
191 if (instance->start_host_formatting && instance->start_host_formatting(instance, host) != 0) {
192 netdata_log_error("EXPORTING: cannot start host formatting for %s", instance->config.name);
193 disable_instance(instance);
194 }
195 } else {
196 instance->skip_host = 1;
197 }
198 }
199 }
200 }
201
202 /**
203 * Start chart formatting for every connector instance's buffer
204 *
205 * @param engine an engine data structure.
206 * @param st a chart.
207 */
208 void start_chart_formatting(struct engine *engine, RRDSET *st)
209 {
210 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
211 if (instance->scheduled && !instance->skip_host) {
212 if (rrdset_is_exportable(instance, st)) {
213 if (instance->start_chart_formatting && instance->start_chart_formatting(instance, st) != 0) {
214 netdata_log_error("EXPORTING: cannot start chart formatting for %s", instance->config.name);
215 disable_instance(instance);
216 }
217 } else {
218 instance->skip_chart = 1;
219 }
220 }
221 }
222 }
223
224 /**
225 * Format metric for every connector instance's buffer
226 *
227 * @param engine an engine data structure.
228 * @param rd a dimension(metric) in the Netdata database.
229 */
230 void metric_formatting(struct engine *engine, RRDDIM *rd)
231 {
232 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
233 if (instance->scheduled && !instance->skip_host && !instance->skip_chart) {
234 if (instance->metric_formatting && instance->metric_formatting(instance, rd) != 0) {
235 netdata_log_error("EXPORTING: cannot format metric for %s", instance->config.name);
236 disable_instance(instance);
237 continue;
238 }
239 instance->stats.buffered_metrics++;
240 }
241 }
242 }
243
244 /**
245 * End chart formatting for every connector instance's buffer
246 *
247 * @param engine an engine data structure.
248 * @param a chart.
249 */
250 void end_chart_formatting(struct engine *engine, RRDSET *st)
251 {
252 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
253 if (instance->scheduled && !instance->skip_host && !instance->skip_chart) {
254 if (instance->end_chart_formatting && instance->end_chart_formatting(instance, st) != 0) {
255 netdata_log_error("EXPORTING: cannot end chart formatting for %s", instance->config.name);
256 disable_instance(instance);
257 continue;
258 }
259 }
260 instance->skip_chart = 0;
261 }
262 }
263
264 /**
265 * Format variables for every connector instance's buffer
266 *
267 * @param engine an engine data structure.
268 * @param host a data collecting host.
269 */
270 void variables_formatting(struct engine *engine, RRDHOST *host)
271 {
272 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
273 if (instance->scheduled && !instance->skip_host && should_send_variables(instance)) {
274 if (instance->variables_formatting && instance->variables_formatting(instance, host) != 0){
275 netdata_log_error("EXPORTING: cannot format variables for %s", instance->config.name);
276 disable_instance(instance);
277 continue;
278 }
279 // sum all variables as one metrics
280 instance->stats.buffered_metrics++;
281 }
282 }
283 }
284
285 /**
286 * End host formatting for every connector instance's buffer
287 *
288 * @param engine an engine data structure.
289 * @param host a data collecting host.
290 */
291 void end_host_formatting(struct engine *engine, RRDHOST *host)
292 {
293 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
294 if (instance->scheduled && !instance->skip_host) {
295 if (instance->end_host_formatting && instance->end_host_formatting(instance, host) != 0) {
296 netdata_log_error("EXPORTING: cannot end host formatting for %s", instance->config.name);
297 disable_instance(instance);
298 continue;
299 }
300 }
301 instance->skip_host = 0;
302 }
303 }
304
305 /**
306 * End batch formatting for every connector instance's buffer
307 *
308 * @param engine an engine data structure.
309 */
310 void end_batch_formatting(struct engine *engine)
311 {
312 for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
313 if (instance->scheduled) {
314 if (instance->end_batch_formatting && instance->end_batch_formatting(instance) != 0) {
315 netdata_log_error("EXPORTING: cannot end batch formatting for %s", instance->config.name);
316 disable_instance(instance);
317 continue;
318 }
319 instance->data_is_ready = 1;
320 netdata_cond_signal(&instance->cond_var);
321 netdata_mutex_unlock(&instance->mutex);
322
323 instance->scheduled = 0;
324 instance->after = instance->before;
325 }
326 }
327 }
328
329 /**
330 * Prepare buffers
331 *
332 * Walk through the Netdata database and fill buffers for every scheduled exporting connector instance according to
333 * configured rules.
334 *
335 * @param engine an engine data structure.
336 */
337 void prepare_buffers(struct engine *engine)
338 {
339 start_batch_formatting(engine);
340
341 rrd_rdlock();
342 RRDHOST *host;
343 rrdhost_foreach_read(host) {
344 start_host_formatting(engine, host);
345 RRDSET *st;
346 rrdset_foreach_read(st, host) {
347 start_chart_formatting(engine, st);
348
349 RRDDIM *rd;
350 rrddim_foreach_read(rd, st)
351 metric_formatting(engine, rd);
352 rrddim_foreach_done(rd);
353
354 end_chart_formatting(engine, st);
355 }
356 rrdset_foreach_done(st);
357 variables_formatting(engine, host);
358 end_host_formatting(engine, host);
359 }
360 rrd_rdunlock();
361
362 end_batch_formatting(engine);
363 }
364
365 /**
366 * Flush a buffer with host labels
367 *
368 * @param instance an instance data structure.
369 * @param host a data collecting host.
370 * @return Always returns 0.
371 */
372 int flush_host_labels(struct instance *instance, RRDHOST *host)
373 {
374 (void)host;
375
376 if (instance->labels_buffer)
377 buffer_flush(instance->labels_buffer);
378
379 return 0;
380 }
381
382 /**
383 * End a batch for a simple connector
384 *
385 * @param instance an instance data structure.
386 * @return Returns 0 on success, 1 on failure.
387 */
388 int simple_connector_end_batch(struct instance *instance)
389 {
390 struct simple_connector_data *simple_connector_data =
391 (struct simple_connector_data *)instance->connector_specific_data;
392 struct stats *stats = &instance->stats;
393
394 BUFFER *instance_buffer = (BUFFER *)instance->buffer;
395 struct simple_connector_buffer *last_buffer = simple_connector_data->last_buffer;
396
397 if (!last_buffer->buffer) {
398 last_buffer->buffer = buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
399 }
400
401 if (last_buffer->used) {
402 // ring buffer is full, reuse the oldest element
403 simple_connector_data->first_buffer = simple_connector_data->first_buffer->next;
404
405 stats->data_lost_events++;
406 stats->lost_metrics += last_buffer->buffered_metrics;
407 stats->lost_bytes += last_buffer->buffered_bytes;
408 }
409
410 // swap buffers
411 BUFFER *tmp_buffer = last_buffer->buffer;
412 last_buffer->buffer = instance_buffer;
413 instance->buffer = instance_buffer = tmp_buffer;
414
415 buffer_flush(instance_buffer);
416
417 if (last_buffer->header)
418 buffer_flush(last_buffer->header);
419 else
420 last_buffer->header = buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
421
422 if (instance->prepare_header)
423 instance->prepare_header(instance);
424
425 // The stats->buffered_metrics is used in the simple connector batch formatting as a variable for the number
426 // of metrics, added in the current iteration, so we are clearing it here. We will use the
427 // simple_connector_data->total_buffered_metrics in the worker to show the statistics.
428 size_t buffered_metrics = (size_t)stats->buffered_metrics;
429 stats->buffered_metrics = 0;
430
431 size_t buffered_bytes = buffer_strlen(last_buffer->buffer);
432
433 last_buffer->buffered_metrics = buffered_metrics;
434 last_buffer->buffered_bytes = buffered_bytes;
435 last_buffer->used++;
436
437 simple_connector_data->total_buffered_metrics += buffered_metrics;
438 stats->buffered_bytes += buffered_bytes;
439
440 simple_connector_data->last_buffer = simple_connector_data->last_buffer->next;
441
442 return 0;
443 }