master
c 1,433 lines 53.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrdcontext-internal.h"
4 #include "database/pattern-array.h"
5
6 #define QUERY_TARGET_MAX_REALLOC_INCREASE 500
7 #define query_target_realloc_size(size, start) \
8 (size) ? ((size) < QUERY_TARGET_MAX_REALLOC_INCREASE ? (size) * 2 : (size) + QUERY_TARGET_MAX_REALLOC_INCREASE) : (start)
9
10 static void query_metric_release(QUERY_TARGET *qt, QUERY_METRIC *qm);
11 static void query_dimension_release(QUERY_DIMENSION *qd);
12 static void query_instance_release(QUERY_INSTANCE *qi);
13 static void query_context_release(QUERY_CONTEXT *qc);
14 static void query_node_release(QUERY_NODE *qn);
15
16 static __thread QUERY_TARGET *thread_qt = NULL;
17 static struct {
18 struct {
19 SPINLOCK spinlock;
20 size_t count;
21 QUERY_TARGET *base;
22 } available;
23
24 struct {
25 SPINLOCK spinlock;
26 size_t count;
27 QUERY_TARGET *base;
28 } used;
29 } query_target_base = {
30 .available = {
31 .spinlock = SPINLOCK_INITIALIZER,
32 .base = NULL,
33 .count = 0,
34 },
35 .used = {
36 .spinlock = SPINLOCK_INITIALIZER,
37 .base = NULL,
38 .count = 0,
39 },
40 };
41
42 static void query_target_destroy(QUERY_TARGET *qt) {
43 __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->query.size * sizeof(*qt->query.array), __ATOMIC_RELAXED);
44 freez(qt->query.array);
45
46 __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->dimensions.size * sizeof(*qt->dimensions.array), __ATOMIC_RELAXED);
47 freez(qt->dimensions.array);
48
49 __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->instances.size * sizeof(*qt->instances.array), __ATOMIC_RELAXED);
50 freez(qt->instances.array);
51
52 __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->contexts.size * sizeof(*qt->contexts.array), __ATOMIC_RELAXED);
53 freez(qt->contexts.array);
54
55 __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->nodes.size * sizeof(*qt->nodes.array), __ATOMIC_RELAXED);
56 freez(qt->nodes.array);
57
58 freez(qt);
59 }
60
61 void query_target_release(QUERY_TARGET *qt) {
62 if(unlikely(!qt)) return;
63
64 internal_fatal(!qt->internal.used, "QUERY TARGET: qt to be released is not used");
65
66 simple_pattern_free(qt->nodes.scope_pattern);
67 qt->nodes.scope_pattern = NULL;
68
69 simple_pattern_free(qt->nodes.pattern);
70 qt->nodes.pattern = NULL;
71
72 simple_pattern_free(qt->contexts.scope_pattern);
73 qt->contexts.scope_pattern = NULL;
74
75 simple_pattern_free(qt->contexts.pattern);
76 qt->contexts.pattern = NULL;
77
78 simple_pattern_free(qt->instances.pattern);
79 qt->instances.pattern = NULL;
80
81 simple_pattern_free(qt->instances.scope_pattern);
82 qt->instances.scope_pattern = NULL;
83
84 simple_pattern_free(qt->instances.chart_label_key_pattern);
85 qt->instances.chart_label_key_pattern = NULL;
86
87 simple_pattern_free(qt->instances.scope_chart_label_key_pattern);
88 qt->instances.scope_chart_label_key_pattern = NULL;
89
90 simple_pattern_free(qt->instances.labels_pattern);
91 qt->instances.labels_pattern = NULL;
92
93 simple_pattern_free(qt->instances.scope_labels_pattern);
94 qt->instances.scope_labels_pattern = NULL;
95
96 pattern_array_free(qt->instances.labels_pa);
97 qt->instances.labels_pa = NULL;
98
99 pattern_array_free(qt->instances.scope_labels_pa);
100 qt->instances.scope_labels_pa = NULL;
101
102 simple_pattern_free(qt->query.pattern);
103 qt->query.pattern = NULL;
104
105 simple_pattern_free(qt->dimensions.scope_pattern);
106 qt->dimensions.scope_pattern = NULL;
107
108 // release the query
109 for(size_t i = 0, used = qt->query.used; i < used ;i++) {
110 QUERY_METRIC *qm = query_metric(qt, i);
111 query_metric_release(qt, qm);
112 }
113 qt->query.used = 0;
114
115 // release the dimensions
116 for(size_t i = 0, used = qt->dimensions.used; i < used ; i++) {
117 QUERY_DIMENSION *qd = query_dimension(qt, i);
118 query_dimension_release(qd);
119 }
120 qt->dimensions.used = 0;
121
122 // release the instances
123 for(size_t i = 0, used = qt->instances.used; i < used ;i++) {
124 QUERY_INSTANCE *qi = query_instance(qt, i);
125 query_instance_release(qi);
126 }
127 qt->instances.used = 0;
128
129 // release the contexts
130 for(size_t i = 0, used = qt->contexts.used; i < used ;i++) {
131 QUERY_CONTEXT *qc = query_context(qt, i);
132 rrdcontext_release(qc->rca);
133 qc->rca = NULL;
134 }
135 qt->contexts.used = 0;
136
137 // release the nodes
138 for(size_t i = 0, used = qt->nodes.used; i < used ; i++) {
139 QUERY_NODE *qn = query_node(qt, i);
140 query_node_release(qn);
141 }
142 qt->nodes.used = 0;
143
144 qt->db.minimum_latest_update_every_s = 0;
145 qt->db.first_time_s = 0;
146 qt->db.last_time_s = 0;
147
148 for(size_t g = 0; g < MAX_QUERY_GROUP_BY_PASSES ;g++)
149 qt->group_by[g].used = 0;
150
151 qt->id[0] = '\0';
152
153 spinlock_lock(&query_target_base.used.spinlock);
154 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(query_target_base.used.base, qt, internal.prev, internal.next);
155 query_target_base.used.count--;
156 spinlock_unlock(&query_target_base.used.spinlock);
157
158 qt->internal.used = false;
159 thread_qt = NULL;
160
161 if (qt->internal.queries > 1000) {
162 query_target_destroy(qt);
163 }
164 else {
165 spinlock_lock(&query_target_base.available.spinlock);
166 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(query_target_base.available.base, qt, internal.prev, internal.next);
167 query_target_base.available.count++;
168 spinlock_unlock(&query_target_base.available.spinlock);
169 }
170 }
171
172 static QUERY_TARGET *query_target_get(void) {
173 spinlock_lock(&query_target_base.available.spinlock);
174 QUERY_TARGET *qt = query_target_base.available.base;
175 if (qt) {
176 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(query_target_base.available.base, qt, internal.prev, internal.next);
177 query_target_base.available.count--;
178 }
179 spinlock_unlock(&query_target_base.available.spinlock);
180
181 if(unlikely(!qt))
182 qt = callocz(1, sizeof(*qt));
183
184 spinlock_lock(&query_target_base.used.spinlock);
185 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(query_target_base.used.base, qt, internal.prev, internal.next);
186 query_target_base.used.count++;
187 spinlock_unlock(&query_target_base.used.spinlock);
188
189 qt->internal.used = true;
190 qt->internal.queries++;
191 thread_qt = qt;
192
193 return qt;
194 }
195
196 // this is used to release a query target from a cancelled thread
197 void query_target_free(void) {
198 query_target_release(thread_qt);
199 }
200
201 // ----------------------------------------------------------------------------
202 // query API
203
204 typedef struct query_target_locals {
205 time_t start_s;
206
207 QUERY_TARGET *qt;
208
209 RRDSET *st;
210
211 const char *scope_nodes;
212 const char *scope_contexts;
213 const char *scope_instances;
214 const char *scope_labels;
215 const char *scope_dimensions;
216
217 const char *nodes;
218 const char *contexts;
219 const char *instances;
220 const char *dimensions;
221 const char *chart_label_key;
222 const char *labels;
223 const char *alerts;
224
225 long long after;
226 long long before;
227 bool match_ids;
228 bool match_names;
229
230 size_t metrics_skipped_due_to_not_matching_timeframe;
231
232 char host_node_id_str[UUID_STR_LEN];
233 QUERY_NODE *qn; // temp to pass on callbacks, ignore otherwise - no need to free
234 } QUERY_TARGET_LOCALS;
235
236 struct storage *query_metric_storage_engine(QUERY_TARGET *qt, QUERY_METRIC *qm, size_t tier) {
237 QUERY_NODE *qn = query_node(qt, qm->link.query_node_id);
238 return qn->rrdhost->db[tier].eng;
239 }
240
241 static inline void query_metric_release(QUERY_TARGET *qt, QUERY_METRIC *qm) {
242 qm->plan.used = 0;
243
244 // reset the tiers
245 for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
246 if(qm->tiers[tier].smh) {
247 STORAGE_ENGINE *eng = query_metric_storage_engine(qt, qm, tier);
248 eng->api.metric_release(qm->tiers[tier].smh);
249 qm->tiers[tier].smh = NULL;
250 }
251 }
252 }
253
254 static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CONTEXT *qc,
255 QUERY_INSTANCE *qi, size_t qd_slot, RRDMETRIC *rm, RRDR_DIMENSION_FLAGS options) {
256 QUERY_TARGET *qt = qtl->qt;
257 RRDINSTANCE *ri = rm->ri;
258
259 time_t common_first_time_s = 0;
260 time_t common_last_time_s = 0;
261 time_t common_update_every_s = 0;
262 size_t tiers_added = 0;
263
264 struct {
265 STORAGE_ENGINE *eng;
266 STORAGE_METRIC_HANDLE *smh;
267 time_t db_first_time_s;
268 time_t db_last_time_s;
269 time_t db_update_every_s;
270 } tier_retention[nd_profile.storage_tiers];
271
272 RRDDIM *rd = rrdmetric_rrddim_get_and_lock(rm);
273 bool values_stored_as_rates = rrdmetric_algorithm_atomic_load(rm) == RRD_ALGORITHM_INCREMENTAL;
274
275 for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
276 STORAGE_ENGINE *eng = qn->rrdhost->db[tier].eng;
277 tier_retention[tier].eng = eng;
278 tier_retention[tier].db_update_every_s = (time_t) (qn->rrdhost->db[tier].tier_grouping * ri->update_every_s);
279
280 if(rd && rd->tiers[tier].smh)
281 tier_retention[tier].smh = eng->api.metric_dup(rd->tiers[tier].smh);
282 else
283 tier_retention[tier].smh = eng->api.metric_get_by_id(qn->rrdhost->db[tier].si, rm->uuid);
284
285 if(tier_retention[tier].smh) {
286 tier_retention[tier].db_first_time_s = storage_engine_oldest_time_s(tier_retention[tier].eng->seb, tier_retention[tier].smh);
287 tier_retention[tier].db_last_time_s = storage_engine_latest_time_s(tier_retention[tier].eng->seb, tier_retention[tier].smh);
288
289 if(!common_first_time_s)
290 common_first_time_s = tier_retention[tier].db_first_time_s;
291 else if(tier_retention[tier].db_first_time_s)
292 common_first_time_s = MIN(common_first_time_s, tier_retention[tier].db_first_time_s);
293
294 if(!common_last_time_s)
295 common_last_time_s = tier_retention[tier].db_last_time_s;
296 else
297 common_last_time_s = MAX(common_last_time_s, tier_retention[tier].db_last_time_s);
298
299 if(!common_update_every_s)
300 common_update_every_s = tier_retention[tier].db_update_every_s;
301 else if(tier_retention[tier].db_update_every_s)
302 common_update_every_s = MIN(common_update_every_s, tier_retention[tier].db_update_every_s);
303
304 tiers_added++;
305 }
306 else {
307 tier_retention[tier].db_first_time_s = 0;
308 tier_retention[tier].db_last_time_s = 0;
309 tier_retention[tier].db_update_every_s = 0;
310 }
311 }
312
313 rrdmetric_rrddim_unlock(rd);
314
315 for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
316 if(!qt->db.tiers[tier].update_every || (tier_retention[tier].db_update_every_s && tier_retention[tier].db_update_every_s < qt->db.tiers[tier].update_every))
317 qt->db.tiers[tier].update_every = tier_retention[tier].db_update_every_s;
318
319 if(!qt->db.tiers[tier].retention.first_time_s || (tier_retention[tier].db_first_time_s && tier_retention[tier].db_first_time_s < qt->db.tiers[tier].retention.first_time_s))
320 qt->db.tiers[tier].retention.first_time_s = tier_retention[tier].db_first_time_s;
321
322 if(!qt->db.tiers[tier].retention.last_time_s || (tier_retention[tier].db_last_time_s && tier_retention[tier].db_last_time_s > qt->db.tiers[tier].retention.last_time_s))
323 qt->db.tiers[tier].retention.last_time_s = tier_retention[tier].db_last_time_s;
324 }
325
326 bool timeframe_matches =
327 (tiers_added &&
328 query_matches_retention(qt->window.after, qt->window.before, common_first_time_s, common_last_time_s, common_update_every_s))
329 ? true : false;
330
331 if(timeframe_matches) {
332 if(ri->rrdset)
333 ri->rrdset->last_accessed_time_s = qtl->start_s;
334
335 if (qt->query.used == qt->query.size) {
336 size_t old_mem = qt->query.size * sizeof(*qt->query.array);
337 qt->query.size = query_target_realloc_size(qt->query.size, 4);
338 size_t new_mem = qt->query.size * sizeof(*qt->query.array);
339 qt->query.array = reallocz(qt->query.array, new_mem);
340
341 __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
342 }
343 QUERY_METRIC *qm = &qt->query.array[qt->query.used++];
344 memset(qm, 0, sizeof(*qm));
345
346 qm->status = options;
347 qm->values_stored_as_rates = values_stored_as_rates;
348
349 qm->link.query_node_id = qn->slot;
350 qm->link.query_context_id = qc->slot;
351 qm->link.query_instance_id = qi->slot;
352 qm->link.query_dimension_id = qd_slot;
353
354 if (!qt->db.first_time_s || common_first_time_s < qt->db.first_time_s)
355 qt->db.first_time_s = common_first_time_s;
356
357 if (!qt->db.last_time_s || common_last_time_s > qt->db.last_time_s)
358 qt->db.last_time_s = common_last_time_s;
359
360 for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
361 internal_fatal(tier_retention[tier].eng != query_metric_storage_engine(qt, qm, tier), "QUERY TARGET: storage engine mismatch");
362 qm->tiers[tier].smh = tier_retention[tier].smh;
363 qm->tiers[tier].db_first_time_s = tier_retention[tier].db_first_time_s;
364 qm->tiers[tier].db_last_time_s = tier_retention[tier].db_last_time_s;
365 qm->tiers[tier].db_update_every_s = tier_retention[tier].db_update_every_s;
366 }
367
368 return true;
369 }
370
371 // cleanup anything we allocated to the retention we will not use
372 for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
373 if (tier_retention[tier].smh) {
374 tier_retention[tier].eng->api.metric_release(tier_retention[tier].smh);
375 tier_retention[tier].smh = NULL;
376 }
377 }
378
379 return false;
380 }
381
382 static inline bool rrdmetric_retention_matches_query(QUERY_TARGET *qt, RRDMETRIC *rm, time_t now_s) {
383 time_t first_time_s = rm->first_time_s;
384 time_t last_time_s = rrd_flag_is_collected(rm) ? now_s : rm->last_time_s;
385 time_t update_every_s = rm->ri->update_every_s;
386 return query_matches_retention(qt->window.after, qt->window.before, first_time_s, last_time_s, update_every_s);
387 }
388
389 static inline void query_dimension_release(QUERY_DIMENSION *qd) {
390 rrdmetric_release(qd->rma);
391 qd->rma = NULL;
392 }
393
394 static QUERY_DIMENSION *query_dimension_allocate(QUERY_TARGET *qt, RRDMETRIC_ACQUIRED *rma, QUERY_STATUS status, size_t priority) {
395 if(qt->dimensions.used == qt->dimensions.size) {
396 size_t old_mem = qt->dimensions.size * sizeof(*qt->dimensions.array);
397 qt->dimensions.size = query_target_realloc_size(qt->dimensions.size, 4);
398 size_t new_mem = qt->dimensions.size * sizeof(*qt->dimensions.array);
399 qt->dimensions.array = reallocz(qt->dimensions.array, new_mem);
400
401 __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
402 }
403 QUERY_DIMENSION *qd = &qt->dimensions.array[qt->dimensions.used];
404 memset(qd, 0, sizeof(*qd));
405
406 qd->slot = qt->dimensions.used++;
407 qd->rma = rrdmetric_acquired_dup(rma);
408 qd->status = status;
409 qd->priority = priority;
410
411 return qd;
412 }
413
414 static bool query_dimension_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CONTEXT *qc, QUERY_INSTANCE *qi,
415 RRDMETRIC_ACQUIRED *rma, bool queryable_instance, size_t *metrics_added, size_t priority) {
416 QUERY_TARGET *qt = qtl->qt;
417
418 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
419 if(rrd_flag_is_deleted(rm))
420 return false;
421
422 // Check scope_dimensions first - if it doesn't match, skip entirely
423 if(qt->dimensions.scope_pattern) {
424 SIMPLE_PATTERN_RESULT ret = SP_NOT_MATCHED;
425
426 if(qtl->match_ids)
427 ret = simple_pattern_matches_string_extract(qt->dimensions.scope_pattern, rm->id, NULL, 0);
428
429 if(ret == SP_NOT_MATCHED && qtl->match_names && (rm->name != rm->id || !qtl->match_ids))
430 ret = simple_pattern_matches_string_extract(qt->dimensions.scope_pattern, rm->name, NULL, 0);
431
432 if(ret != SP_MATCHED_POSITIVE)
433 return false; // Skip this dimension entirely - not in scope
434 }
435
436 QUERY_STATUS status = QUERY_STATUS_NONE;
437
438 bool undo = false;
439 if(!queryable_instance) {
440 if(rrdmetric_retention_matches_query(qt, rm, qtl->start_s)) {
441 qi->metrics.excluded++;
442 qc->metrics.excluded++;
443 qn->metrics.excluded++;
444 status |= QUERY_STATUS_EXCLUDED;
445 }
446 else
447 undo = true;
448 }
449 else {
450 RRDR_DIMENSION_FLAGS options = RRDR_DIMENSION_DEFAULT;
451 bool needed = false;
452
453 if (qt->query.pattern) {
454 // the user asked for specific dimensions
455
456 SIMPLE_PATTERN_RESULT ret = SP_NOT_MATCHED;
457
458 if(qtl->match_ids)
459 ret = simple_pattern_matches_string_extract(qt->query.pattern, rm->id, NULL, 0);
460
461 if(ret == SP_NOT_MATCHED && qtl->match_names && (rm->name != rm->id || !qtl->match_ids))
462 ret = simple_pattern_matches_string_extract(qt->query.pattern, rm->name, NULL, 0);
463
464 if(ret == SP_MATCHED_POSITIVE) {
465 needed = true;
466 options |= RRDR_DIMENSION_SELECTED | RRDR_DIMENSION_NONZERO;
467 }
468 else {
469 // the user selection does not match this dimension
470 // but, we may still need to query it
471
472 if (query_target_needs_all_dimensions(qt)) {
473 // this is percentage calculation
474 // so, we need this dimension to calculate the percentage
475 needed = true;
476 options |= RRDR_DIMENSION_HIDDEN;
477 }
478 else {
479 // the user did not select this dimension
480 // and the calculation is not percentage
481 // so, no need to query it
482 ;
483 }
484 }
485 }
486 else {
487 // we don't have a dimensions pattern
488 // so this is a selected dimension
489 // if it is not hidden
490 RRDDIM *rd = rrdmetric_rrddim_get_and_lock(rm);
491 bool hidden = rrd_flag_check(rm, RRD_FLAG_HIDDEN) || (rd && rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN));
492 rrdmetric_rrddim_unlock(rd);
493
494 if(hidden) {
495 // this is a hidden dimension
496 // we don't need to query it
497 status |= QUERY_STATUS_DIMENSION_HIDDEN;
498 options |= RRDR_DIMENSION_HIDDEN;
499
500 if (query_target_needs_all_dimensions(qt)) {
501 // this is percentage calculation
502 // so, we need this dimension to calculate the percentage
503 needed = true;
504 }
505 }
506 else {
507 // this is a not hidden dimension
508 // and the user did not provide any selection for dimensions
509 // so, we need to query it
510 needed = true;
511 options |= RRDR_DIMENSION_SELECTED;
512 }
513 }
514
515 if (needed) {
516 if(query_metric_add(qtl, qn, qc, qi, qt->dimensions.used, rm, options)) {
517 (*metrics_added)++;
518
519 qi->metrics.selected++;
520 qc->metrics.selected++;
521 qn->metrics.selected++;
522 }
523 else {
524 undo = true;
525 qtl->metrics_skipped_due_to_not_matching_timeframe++;
526 }
527 }
528 else if(rrdmetric_retention_matches_query(qt, rm, qtl->start_s)) {
529 qi->metrics.excluded++;
530 qc->metrics.excluded++;
531 qn->metrics.excluded++;
532 status |= QUERY_STATUS_EXCLUDED;
533 }
534 else
535 undo = true;
536 }
537
538 if(undo)
539 return false;
540
541 query_dimension_allocate(qt, rma, status, priority);
542 return true;
543 }
544
545 static inline STRING *rrdinstance_create_id_fqdn_v1(RRDINSTANCE_ACQUIRED *ria) {
546 if(unlikely(!ria))
547 return NULL;
548
549 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
550 return string_dup(ri->id);
551 }
552
553 static inline STRING *rrdinstance_create_name_fqdn_v1(RRDINSTANCE_ACQUIRED *ria) {
554 if(unlikely(!ria))
555 return NULL;
556
557 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
558 return string_dup(ri->name);
559 }
560
561 static inline STRING *rrdinstance_create_id_fqdn_v2(RRDINSTANCE_ACQUIRED *ria) {
562 if(unlikely(!ria))
563 return NULL;
564
565 char buffer[RRD_ID_LENGTH_MAX + 1];
566
567 RRDHOST *host = rrdinstance_acquired_rrdhost(ria);
568 snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s@%s", rrdinstance_acquired_id(ria), host->machine_guid);
569 return string_strdupz(buffer);
570 }
571
572 static inline STRING *rrdinstance_create_name_fqdn_v2(RRDINSTANCE_ACQUIRED *ria) {
573 if(unlikely(!ria))
574 return NULL;
575
576 char buffer[RRD_ID_LENGTH_MAX + 1];
577
578 RRDHOST *host = rrdinstance_acquired_rrdhost(ria);
579 snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s@%s", rrdinstance_acquired_name(ria), rrdhost_hostname(host));
580 return string_strdupz(buffer);
581 }
582
583 inline STRING *query_instance_id_fqdn(QUERY_INSTANCE *qi, size_t version) {
584 if(!qi->id_fqdn) {
585 if (version <= 1)
586 qi->id_fqdn = rrdinstance_create_id_fqdn_v1(qi->ria);
587 else
588 qi->id_fqdn = rrdinstance_create_id_fqdn_v2(qi->ria);
589 }
590
591 return qi->id_fqdn;
592 }
593
594 inline STRING *query_instance_name_fqdn(QUERY_INSTANCE *qi, size_t version) {
595 if(!qi->name_fqdn) {
596 if (version <= 1)
597 qi->name_fqdn = rrdinstance_create_name_fqdn_v1(qi->ria);
598 else
599 qi->name_fqdn = rrdinstance_create_name_fqdn_v2(qi->ria);
600 }
601
602 return qi->name_fqdn;
603 }
604
605 RRDSET *rrdinstance_acquired_rrdset(RRDINSTANCE_ACQUIRED *ria) {
606 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
607 return ri->rrdset;
608 }
609
610 const char *rrdcontext_acquired_units(RRDCONTEXT_ACQUIRED *rca) {
611 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
612 return string2str(rc->units);
613 }
614
615 RRDSET_TYPE rrdcontext_acquired_chart_type(RRDCONTEXT_ACQUIRED *rca) {
616 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
617 return rc->chart_type;
618 }
619
620 const char *rrdcontext_acquired_title(RRDCONTEXT_ACQUIRED *rca) {
621 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
622 return string2str(rc->title);
623 }
624
625 static void query_target_eval_instance_rrdcalc(QUERY_TARGET_LOCALS *qtl __maybe_unused,
626 QUERY_NODE *qn, QUERY_CONTEXT *qc, QUERY_INSTANCE *qi) {
627 RRDSET *st = rrdinstance_acquired_rrdset(qi->ria);
628 if (st) {
629 rw_spinlock_read_lock(&st->alerts.spinlock);
630 for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) {
631 switch(rc->status) {
632 case RRDCALC_STATUS_CLEAR:
633 qi->alerts.clear++;
634 qc->alerts.clear++;
635 qn->alerts.clear++;
636 break;
637
638 case RRDCALC_STATUS_WARNING:
639 qi->alerts.warning++;
640 qc->alerts.warning++;
641 qn->alerts.warning++;
642 break;
643
644 case RRDCALC_STATUS_CRITICAL:
645 qi->alerts.critical++;
646 qc->alerts.critical++;
647 qn->alerts.critical++;
648 break;
649
650 default:
651 case RRDCALC_STATUS_UNINITIALIZED:
652 case RRDCALC_STATUS_UNDEFINED:
653 case RRDCALC_STATUS_REMOVED:
654 qi->alerts.other++;
655 qc->alerts.other++;
656 qn->alerts.other++;
657 break;
658 }
659 }
660 rw_spinlock_read_unlock(&st->alerts.spinlock);
661 }
662 }
663
664 static bool query_target_match_alert_pattern(RRDINSTANCE_ACQUIRED *ria, SIMPLE_PATTERN *pattern) {
665 if(!pattern)
666 return true;
667
668 RRDSET *st = rrdinstance_acquired_rrdset(ria);
669 if (!st)
670 return false;
671
672 BUFFER *wb = NULL;
673 bool matched = false;
674 rw_spinlock_read_lock(&st->alerts.spinlock);
675 if (st->alerts.base) {
676 for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) {
677 SIMPLE_PATTERN_RESULT ret = simple_pattern_matches_string_extract(pattern, rc->config.name, NULL, 0);
678
679 if(ret == SP_MATCHED_POSITIVE) {
680 matched = true;
681 break;
682 }
683 else if(ret == SP_MATCHED_NEGATIVE)
684 break;
685
686 if (!wb)
687 wb = buffer_create(0, NULL);
688 else
689 buffer_flush(wb);
690
691 buffer_fast_strcat(wb, string2str(rc->config.name), string_strlen(rc->config.name));
692 buffer_fast_strcat(wb, ":", 1);
693 buffer_strcat(wb, rrdcalc_status2string(rc->status));
694
695 ret = simple_pattern_matches_buffer_extract(pattern, wb, NULL, 0);
696
697 if(ret == SP_MATCHED_POSITIVE) {
698 matched = true;
699 break;
700 }
701 else if(ret == SP_MATCHED_NEGATIVE)
702 break;
703 }
704 }
705 rw_spinlock_read_unlock(&st->alerts.spinlock);
706
707 buffer_free(wb);
708 return matched;
709 }
710
711 static inline void query_instance_strings_free(QUERY_INSTANCE *qi) {
712 string_freez(qi->id_fqdn);
713 qi->id_fqdn = NULL;
714
715 string_freez(qi->name_fqdn);
716 qi->name_fqdn = NULL;
717 }
718
719 static inline void query_instance_release(QUERY_INSTANCE *qi) {
720 if(qi->ria) {
721 rrdinstance_release(qi->ria);
722 qi->ria = NULL;
723 }
724
725 query_instance_strings_free(qi);
726 }
727
728 static inline QUERY_INSTANCE *query_instance_allocate(QUERY_TARGET *qt, RRDINSTANCE_ACQUIRED *ria, size_t qn_slot) {
729 if(qt->instances.used == qt->instances.size) {
730 size_t old_mem = qt->instances.size * sizeof(*qt->instances.array);
731 qt->instances.size = query_target_realloc_size(qt->instances.size, 2);
732 size_t new_mem = qt->instances.size * sizeof(*qt->instances.array);
733 qt->instances.array = reallocz(qt->instances.array, new_mem);
734
735 __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
736 }
737 QUERY_INSTANCE *qi = &qt->instances.array[qt->instances.used];
738 memset(qi, 0, sizeof(*qi));
739
740 qi->slot = qt->instances.used;
741 qt->instances.used++;
742 qi->ria = rrdinstance_acquired_dup(ria);
743 qi->query_host_id = qn_slot;
744
745 return qi;
746 }
747
748 static inline SIMPLE_PATTERN_RESULT query_instance_matches(QUERY_INSTANCE *qi,
749 RRDINSTANCE *ri,
750 SIMPLE_PATTERN *instances_sp,
751 bool match_ids,
752 bool match_names,
753 size_t version,
754 char *host_node_id_str) {
755 SIMPLE_PATTERN_RESULT ret = SP_MATCHED_POSITIVE;
756
757 if(instances_sp) {
758 ret = SP_NOT_MATCHED;
759
760 if(match_ids)
761 ret = simple_pattern_matches_string_extract(instances_sp, ri->id, NULL, 0);
762 if (ret == SP_NOT_MATCHED && match_names && (ri->name != ri->id || !match_ids))
763 ret = simple_pattern_matches_string_extract(instances_sp, ri->name, NULL, 0);
764 if (ret == SP_NOT_MATCHED && match_ids)
765 ret = simple_pattern_matches_string_extract(instances_sp, query_instance_id_fqdn(qi, version), NULL, 0);
766 if (ret == SP_NOT_MATCHED && match_names)
767 ret = simple_pattern_matches_string_extract(instances_sp, query_instance_name_fqdn(qi, version), NULL, 0);
768
769 if (ret == SP_NOT_MATCHED && match_ids && host_node_id_str[0]) {
770 char buffer[RRD_ID_LENGTH_MAX + 1];
771 snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s@%s", rrdinstance_acquired_id(qi->ria), host_node_id_str);
772 ret = simple_pattern_matches_extract(instances_sp, buffer, NULL, 0);
773 }
774 }
775
776 return ret;
777 }
778
779 static inline bool query_instance_matches_labels(
780 RRDINSTANCE *ri,
781 SIMPLE_PATTERN *chart_label_key_sp,
782 struct pattern_array *labels_pa)
783 {
784 RRDLABELS *labels = rrdinstance_labels(ri);
785 if (chart_label_key_sp && rrdlabels_match_simple_pattern_parsed(labels, chart_label_key_sp, '\0', NULL) != SP_MATCHED_POSITIVE)
786 return false;
787
788 if (labels_pa) {
789 return pattern_array_label_match(labels_pa, labels, ':', NULL);
790 }
791
792 return true;
793 }
794
795 static bool query_instance_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CONTEXT *qc,
796 RRDINSTANCE_ACQUIRED *ria, bool queryable_instance, bool filter_instances) {
797 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
798 if(rrd_flag_is_deleted(ri))
799 return false;
800
801 QUERY_TARGET *qt = qtl->qt;
802 QUERY_INSTANCE *qi = query_instance_allocate(qt, ria, qn->slot);
803
804 if(queryable_instance && filter_instances)
805 queryable_instance = (SP_MATCHED_POSITIVE == query_instance_matches(
806 qi, ri, qt->instances.pattern, qtl->match_ids, qtl->match_names, qt->request.version, qtl->host_node_id_str));
807
808 if(queryable_instance)
809 queryable_instance = query_instance_matches_labels(
810 ri,
811 qt->instances.chart_label_key_pattern,
812 qt->instances.labels_pa);
813
814 if(queryable_instance) {
815 if(qt->instances.alerts_pattern && !query_target_match_alert_pattern(ria, qt->instances.alerts_pattern))
816 queryable_instance = false;
817 }
818
819 if(queryable_instance && qt->request.version >= 2)
820 query_target_eval_instance_rrdcalc(qtl, qn, qc, qi);
821
822 size_t dimensions_added = 0, metrics_added = 0, priority = 0;
823
824 if(unlikely(qt->request.rma)) {
825 if(query_dimension_add(qtl, qn, qc, qi, qt->request.rma, queryable_instance, &metrics_added, priority++))
826 dimensions_added++;
827 }
828 else {
829 RRDMETRIC *rm;
830 dfe_start_read(ri->rrdmetrics, rm) {
831 if(query_dimension_add(qtl, qn, qc, qi, (RRDMETRIC_ACQUIRED *) rm_dfe.item,
832 queryable_instance, &metrics_added, priority++))
833 dimensions_added++;
834 }
835 dfe_done(rm);
836 }
837
838 if(!dimensions_added) {
839 qt->instances.used--;
840 query_instance_release(qi);
841 return false;
842 }
843 else {
844 if(metrics_added) {
845 if(qt->db.minimum_latest_update_every_s == 0 || ri->update_every_s < qt->db.minimum_latest_update_every_s)
846 qt->db.minimum_latest_update_every_s = ri->update_every_s;
847
848 qc->instances.selected++;
849 qn->instances.selected++;
850 }
851 else {
852 qc->instances.excluded++;
853 qn->instances.excluded++;
854 }
855 }
856
857 return true;
858 }
859
860 static inline void query_context_release(QUERY_CONTEXT *qc) {
861 rrdcontext_release(qc->rca);
862 qc->rca = NULL;
863 }
864
865 static inline QUERY_CONTEXT *query_context_allocate(QUERY_TARGET *qt, RRDCONTEXT_ACQUIRED *rca) {
866 if(qt->contexts.used == qt->contexts.size) {
867 size_t old_mem = qt->contexts.size * sizeof(*qt->contexts.array);
868 qt->contexts.size = query_target_realloc_size(qt->contexts.size, 2);
869 size_t new_mem = qt->contexts.size * sizeof(*qt->contexts.array);
870 qt->contexts.array = reallocz(qt->contexts.array, new_mem);
871
872 __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
873 }
874 QUERY_CONTEXT *qc = &qt->contexts.array[qt->contexts.used];
875 memset(qc, 0, sizeof(*qc));
876 qc->slot = qt->contexts.used++;
877 qc->rca = rrdcontext_acquired_dup(rca);
878
879 return qc;
880 }
881
882 static ssize_t query_scope_foreach_instance(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CONTEXT *qc,
883 RRDCONTEXT_ACQUIRED *rca, bool queryable_context) {
884 QUERY_TARGET *qt = qtl->qt;
885 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
886 ssize_t added = 0;
887
888 if(unlikely(qt->request.ria)) {
889 // Single instance requested
890 RRDINSTANCE *ri = rrdinstance_acquired_value(qt->request.ria);
891
892 // Check scope_instances
893 if(qt->instances.scope_pattern) {
894 QUERY_INSTANCE qi = { .ria = qt->request.ria };
895 SIMPLE_PATTERN_RESULT ret = query_instance_matches(&qi, ri,
896 qt->instances.scope_pattern, qtl->match_ids, qtl->match_names,
897 qt->request.version, qtl->host_node_id_str);
898 query_instance_strings_free(&qi);
899 if(ret != SP_MATCHED_POSITIVE)
900 return 0;
901 }
902
903 // Check scope_labels
904 if(qt->instances.scope_labels_pa || qt->instances.scope_chart_label_key_pattern) {
905 if(!query_instance_matches_labels(ri,
906 qt->instances.scope_chart_label_key_pattern,
907 qt->instances.scope_labels_pa))
908 return 0;
909 }
910
911 if(query_instance_add(qtl, qn, qc, qt->request.ria, queryable_context, false))
912 added++;
913 }
914 else if(unlikely(qtl->st && qtl->st->rrdcontexts.rrdcontext == rca)) {
915 // Single chart requested
916 RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_get_and_acquire_item(
917 rc->rrdinstances, string2str(qtl->st->id));
918 if(unlikely(!ria))
919 return 0;
920
921 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
922
923 // Check scope_instances
924 if(qt->instances.scope_pattern) {
925 QUERY_INSTANCE qi = { .ria = ria };
926 SIMPLE_PATTERN_RESULT ret = query_instance_matches(&qi, ri,
927 qt->instances.scope_pattern, qtl->match_ids, qtl->match_names,
928 qt->request.version, qtl->host_node_id_str);
929 query_instance_strings_free(&qi);
930 if(ret != SP_MATCHED_POSITIVE) {
931 rrdinstance_release(ria);
932 return 0;
933 }
934 }
935
936 // Check scope_labels
937 if(qt->instances.scope_labels_pa || qt->instances.scope_chart_label_key_pattern) {
938 if(!query_instance_matches_labels(ri,
939 qt->instances.scope_chart_label_key_pattern,
940 qt->instances.scope_labels_pa)) {
941 rrdinstance_release(ria);
942 return 0;
943 }
944 }
945
946 if(query_instance_add(qtl, qn, qc, ria, queryable_context, false))
947 added++;
948
949 rrdinstance_release(ria);
950 }
951 else {
952 // Pattern query - iterate through all instances
953 RRDINSTANCE *ri;
954 dfe_start_read(rc->rrdinstances, ri) {
955 if(rrd_flag_is_deleted(ri))
956 continue;
957
958 RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *) ri_dfe.item;
959
960 // Check scope_instances
961 if(qt->instances.scope_pattern) {
962 QUERY_INSTANCE qi = { .ria = ria };
963 SIMPLE_PATTERN_RESULT ret = query_instance_matches(&qi, ri,
964 qt->instances.scope_pattern, qtl->match_ids, qtl->match_names,
965 qt->request.version, qtl->host_node_id_str);
966 query_instance_strings_free(&qi);
967 if(ret != SP_MATCHED_POSITIVE)
968 continue;
969 }
970
971 // Check scope_labels
972 if(qt->instances.scope_labels_pa || qt->instances.scope_chart_label_key_pattern) {
973 if(!query_instance_matches_labels(ri,
974 qt->instances.scope_chart_label_key_pattern,
975 qt->instances.scope_labels_pa))
976 continue;
977 }
978
979 if(query_instance_add(qtl, qn, qc, ria, queryable_context, true))
980 added++;
981 }
982 dfe_done(ri);
983 }
984
985 return added;
986 }
987
988 static ssize_t query_context_add(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context) {
989 QUERY_TARGET_LOCALS *qtl = data;
990
991 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
992 if(rrd_flag_is_deleted(rc))
993 return 0;
994
995 QUERY_NODE *qn = qtl->qn;
996 QUERY_TARGET *qt = qtl->qt;
997 QUERY_CONTEXT *qc = query_context_allocate(qt, rca);
998
999 ssize_t added = query_scope_foreach_instance(qtl, qn, qc, rca, queryable_context);
1000
1001 if(!added) {
1002 query_context_release(qc);
1003 qt->contexts.used--;
1004 return 0;
1005 }
1006
1007 return added;
1008 }
1009
1010 static inline void query_node_release(QUERY_NODE *qn) {
1011 qn->rrdhost = NULL;
1012 }
1013
1014 static inline QUERY_NODE *query_node_allocate(QUERY_TARGET *qt, RRDHOST *host) {
1015 if(qt->nodes.used == qt->nodes.size) {
1016 size_t old_mem = qt->nodes.size * sizeof(*qt->nodes.array);
1017 qt->nodes.size = query_target_realloc_size(qt->nodes.size, 2);
1018 size_t new_mem = qt->nodes.size * sizeof(*qt->nodes.array);
1019 qt->nodes.array = reallocz(qt->nodes.array, new_mem);
1020
1021 __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
1022 }
1023 QUERY_NODE *qn = &qt->nodes.array[qt->nodes.used];
1024 memset(qn, 0, sizeof(*qn));
1025
1026 qn->slot = qt->nodes.used++;
1027 qn->rrdhost = host;
1028
1029 return qn;
1030 }
1031
1032 static ssize_t query_node_add(void *data, RRDHOST *host, bool queryable_host) {
1033 QUERY_TARGET_LOCALS *qtl = data;
1034 QUERY_TARGET *qt = qtl->qt;
1035 QUERY_NODE *qn = query_node_allocate(qt, host);
1036
1037 if(!UUIDiszero(host->node_id)) {
1038 if(!qtl->host_node_id_str[0])
1039 uuid_unparse_lower(host->node_id.uuid, qn->node_id);
1040 else
1041 memcpy(qn->node_id, qtl->host_node_id_str, sizeof(qn->node_id));
1042 }
1043 else
1044 qn->node_id[0] = '\0';
1045
1046 // is the chart given valid?
1047 if(unlikely(qtl->st && (!qtl->st->rrdcontexts.rrdinstance || !qtl->st->rrdcontexts.rrdcontext))) {
1048 netdata_log_error("QUERY TARGET: RRDSET '%s' given, but it is not linked to rrdcontext structures. Linking it now.", rrdset_name(qtl->st));
1049 rrdinstance_from_rrdset(qtl->st);
1050
1051 if(unlikely(qtl->st && (!qtl->st->rrdcontexts.rrdinstance || !qtl->st->rrdcontexts.rrdcontext))) {
1052 netdata_log_error("QUERY TARGET: RRDSET '%s' given, but failed to be linked to rrdcontext structures. Switching to context query.",
1053 rrdset_name(qtl->st));
1054
1055 if (!is_valid_sp(qtl->instances))
1056 qtl->instances = rrdset_name(qtl->st);
1057
1058 qtl->st = NULL;
1059 }
1060 }
1061
1062 qtl->qn = qn;
1063
1064 ssize_t added = 0;
1065 if(unlikely(qt->request.rca)) {
1066 if(query_context_add(qtl, qt->request.rca, true))
1067 added++;
1068 }
1069 else if(unlikely(qtl->st)) {
1070 // single chart data queries
1071 if(query_context_add(qtl, qtl->st->rrdcontexts.rrdcontext, true))
1072 added++;
1073 }
1074 else {
1075 // context pattern queries
1076 added = query_scope_foreach_context(
1077 host, qtl->scope_contexts,
1078 qt->contexts.scope_pattern, qt->contexts.pattern,
1079 query_context_add, queryable_host, qtl);
1080
1081 if(added < 0)
1082 added = 0;
1083 }
1084
1085 qtl->qn = NULL;
1086
1087 if(!added) {
1088 query_node_release(qn);
1089 qt->nodes.used--;
1090 return false;
1091 }
1092
1093 return true;
1094 }
1095
1096 void query_target_generate_name(QUERY_TARGET *qt) {
1097 char options_buffer[100 + 1];
1098 web_client_api_request_data_vX_options_to_string(options_buffer, 100, qt->request.options);
1099
1100 char resampling_buffer[20 + 1] = "";
1101 if(qt->request.resampling_time > 1)
1102 snprintfz(resampling_buffer, 20, "/resampling:%lld", (long long)qt->request.resampling_time);
1103
1104 char tier_buffer[20 + 1] = "";
1105 if(qt->request.options & RRDR_OPTION_SELECTED_TIER)
1106 snprintfz(tier_buffer, 20, "/tier:%zu", qt->request.tier);
1107
1108 if(qt->request.st)
1109 snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "chart://hosts:%s/instance:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s"
1110 , rrdhost_hostname(qt->request.st->rrdhost)
1111 , rrdset_name(qt->request.st)
1112 , (qt->request.dimensions) ? qt->request.dimensions : "*"
1113 , (long long)qt->request.after
1114 , (long long)qt->request.before
1115 , qt->request.points
1116 , time_grouping_tostring(qt->request.time_group_method)
1117 , qt->request.time_group_options ? qt->request.time_group_options : ""
1118 , options_buffer
1119 , resampling_buffer
1120 , tier_buffer
1121 );
1122 else if(qt->request.host && qt->request.rca && qt->request.ria && qt->request.rma)
1123 snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "metric://hosts:%s/context:%s/instance:%s/dimension:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s"
1124 , rrdhost_hostname(qt->request.host)
1125 , rrdcontext_acquired_id(qt->request.rca)
1126 , rrdinstance_acquired_id(qt->request.ria)
1127 , rrdmetric_acquired_id(qt->request.rma)
1128 , (long long)qt->request.after
1129 , (long long)qt->request.before
1130 , qt->request.points
1131 , time_grouping_tostring(qt->request.time_group_method)
1132 , qt->request.time_group_options ? qt->request.time_group_options : ""
1133 , options_buffer
1134 , resampling_buffer
1135 , tier_buffer
1136 );
1137 else if(qt->request.version >= 2)
1138 snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "data_v2://scope_nodes:%s/scope_contexts:%s/scope_instances:%s/scope_labels:%s/scope_dimensions:%s/nodes:%s/contexts:%s/instances:%s/labels:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/time_group:%s%s/options:%s%s%s"
1139 , qt->request.scope_nodes ? qt->request.scope_nodes : "*"
1140 , qt->request.scope_contexts ? qt->request.scope_contexts : "*"
1141 , qt->request.scope_instances ? qt->request.scope_instances : "*"
1142 , qt->request.scope_labels ? qt->request.scope_labels : "*"
1143 , qt->request.scope_dimensions ? qt->request.scope_dimensions : "*"
1144 , qt->request.nodes ? qt->request.nodes : "*"
1145 , (qt->request.contexts) ? qt->request.contexts : "*"
1146 , (qt->request.instances) ? qt->request.instances : "*"
1147 , (qt->request.labels) ? qt->request.labels : "*"
1148 , (qt->request.dimensions) ? qt->request.dimensions : "*"
1149 , (long long)qt->request.after
1150 , (long long)qt->request.before
1151 , qt->request.points
1152 , time_grouping_tostring(qt->request.time_group_method)
1153 , qt->request.time_group_options ? qt->request.time_group_options : ""
1154 , options_buffer
1155 , resampling_buffer
1156 , tier_buffer
1157 );
1158 else
1159 snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "context://hosts:%s/contexts:%s/instances:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s"
1160 , (qt->request.host) ? rrdhost_hostname(qt->request.host) : ((qt->request.nodes) ? qt->request.nodes : "*")
1161 , (qt->request.contexts) ? qt->request.contexts : "*"
1162 , (qt->request.instances) ? qt->request.instances : "*"
1163 , (qt->request.dimensions) ? qt->request.dimensions : "*"
1164 , (long long)qt->request.after
1165 , (long long)qt->request.before
1166 , qt->request.points
1167 , time_grouping_tostring(qt->request.time_group_method)
1168 , qt->request.time_group_options ? qt->request.time_group_options : ""
1169 , options_buffer
1170 , resampling_buffer
1171 , tier_buffer
1172 );
1173
1174 // Sanitize the query ID - safe because qt->id is ASCII-only (from snprintfz)
1175 char buf[MAX_QUERY_TARGET_ID_LENGTH + 1];
1176 text_sanitize((unsigned char *)buf, (const unsigned char *)qt->id, sizeof(buf),
1177 rrd_string_allowed_chars, true, "", NULL);
1178 strcpy(qt->id, buf);
1179 }
1180
1181 QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr) {
1182 //if(!service_running(ABILITY_DATA_QUERIES))
1183 // return NULL;
1184
1185 QUERY_TARGET *qt = query_target_get();
1186
1187 if(!qtr->received_ut)
1188 qtr->received_ut = now_monotonic_usec();
1189
1190 qt->timings.received_ut = qtr->received_ut;
1191
1192 if(qtr->nodes && !qtr->scope_nodes)
1193 qtr->scope_nodes = qtr->nodes;
1194
1195 if(qtr->contexts && !qtr->scope_contexts)
1196 qtr->scope_contexts = qtr->contexts;
1197
1198 // IMPORTANT: old dashboards do not know about scope_instances and scope_labels
1199 // so this code makes non-scope instances and labels to be used as scope.
1200 // Leave it commented!
1201
1202 // if(qtr->instances && !qtr->scope_instances)
1203 // qtr->scope_instances = qtr->instances;
1204 //
1205 // if(qtr->labels && !qtr->scope_labels)
1206 // qtr->scope_labels = qtr->labels;
1207
1208 memset(&qt->db, 0, sizeof(qt->db));
1209 qt->query_points = STORAGE_POINT_UNSET;
1210
1211 // copy the request into query_thread_target
1212 qt->request = *qtr;
1213
1214 query_target_generate_name(qt);
1215 qt->window.after = qt->request.after;
1216 qt->window.before = qt->request.before;
1217
1218 qt->window.options = qt->request.options;
1219 if(query_target_has_percentage_of_group(qt))
1220 qt->window.options &= ~RRDR_OPTION_PERCENTAGE;
1221
1222 qt->internal.relative = rrdr_relative_window_to_absolute_query(&qt->window.after, &qt->window.before
1223 , &qt->window.now, unittest_running
1224 );
1225
1226 // prepare our local variables - we need these across all these functions
1227 QUERY_TARGET_LOCALS qtl = {
1228 .qt = qt,
1229 .start_s = now_realtime_sec(),
1230 .st = qt->request.st,
1231 .scope_nodes = qt->request.scope_nodes,
1232 .scope_contexts = qt->request.scope_contexts,
1233 .scope_instances = qt->request.scope_instances,
1234 .scope_labels = qt->request.scope_labels,
1235 .scope_dimensions = qt->request.scope_dimensions,
1236 .nodes = qt->request.nodes,
1237 .contexts = qt->request.contexts,
1238 .instances = qt->request.instances,
1239 .dimensions = qt->request.dimensions,
1240 .chart_label_key = qt->request.chart_label_key,
1241 .labels = qt->request.labels,
1242 .alerts = qt->request.alerts,
1243 };
1244
1245 RRDHOST *host = qt->request.host;
1246
1247 // prepare all the patterns
1248 qt->nodes.scope_pattern = string_to_simple_pattern(qtl.scope_nodes);
1249 qt->nodes.pattern = string_to_simple_pattern(qtl.nodes);
1250
1251 qt->contexts.pattern = string_to_simple_pattern(qtl.contexts);
1252 qt->contexts.scope_pattern = string_to_simple_pattern(qtl.scope_contexts);
1253
1254 qt->instances.pattern = string_to_simple_pattern(qtl.instances);
1255 qt->instances.scope_pattern = string_to_simple_pattern(qtl.scope_instances);
1256 qt->query.pattern = string_to_simple_pattern(qtl.dimensions);
1257 qt->dimensions.scope_pattern = string_to_simple_pattern(qtl.scope_dimensions);
1258 qt->instances.chart_label_key_pattern = string_to_simple_pattern(qtl.chart_label_key);
1259 qt->instances.scope_chart_label_key_pattern = string_to_simple_pattern(qtl.chart_label_key); // For now, using same as non-scope
1260 qt->instances.labels_pattern = string_to_simple_pattern(qtl.labels);
1261 qt->instances.scope_labels_pattern = string_to_simple_pattern(qtl.scope_labels);
1262 qt->instances.alerts_pattern = string_to_simple_pattern(qtl.alerts);
1263
1264 // Pre-compile pattern arrays for labels
1265 if(qt->instances.labels_pattern)
1266 qt->instances.labels_pa = pattern_array_add_simple_pattern(NULL, qt->instances.labels_pattern, ':');
1267 if(qt->instances.scope_labels_pattern)
1268 qt->instances.scope_labels_pa = pattern_array_add_simple_pattern(NULL, qt->instances.scope_labels_pattern, ':');
1269
1270 qtl.match_ids = qt->request.options & RRDR_OPTION_MATCH_IDS;
1271 qtl.match_names = qt->request.options & RRDR_OPTION_MATCH_NAMES;
1272 if(likely(!qtl.match_ids && !qtl.match_names))
1273 qtl.match_ids = qtl.match_names = true;
1274
1275 // verify that the chart belongs to the host we are interested
1276 if(qtl.st) {
1277 if (!host) {
1278 // It is NULL, set it ourselves.
1279 host = qtl.st->rrdhost;
1280 }
1281 else if (unlikely(host != qtl.st->rrdhost)) {
1282 // Oops! A different host!
1283 netdata_log_error("QUERY TARGET: RRDSET '%s' given does not belong to host '%s'. Switching query host to '%s'",
1284 rrdset_name(qtl.st), rrdhost_hostname(host), rrdhost_hostname(qtl.st->rrdhost));
1285 host = qtl.st->rrdhost;
1286 }
1287 }
1288
1289 if(host) {
1290 if(!UUIDiszero(host->node_id))
1291 uuid_unparse_lower(host->node_id.uuid, qtl.host_node_id_str);
1292 else
1293 qtl.host_node_id_str[0] = '\0';
1294
1295 // single host query
1296 qt->versions.contexts_hard_hash = dictionary_version(host->rrdctx.contexts);
1297 qt->versions.contexts_soft_hash = rrdcontext_queue_version(&host->rrdctx.hub_queue);
1298 qt->versions.alerts_hard_hash = dictionary_version(host->rrdcalc_root_index);
1299 qt->versions.alerts_soft_hash = __atomic_load_n(&host->health_transitions, __ATOMIC_RELAXED);
1300 query_node_add(&qtl, host, true);
1301 qtl.nodes = rrdhost_hostname(host);
1302 }
1303 else
1304 query_scope_foreach_host(qt->nodes.scope_pattern, qt->nodes.pattern,
1305 query_node_add, &qtl,
1306 &qt->versions,
1307 qtl.host_node_id_str);
1308
1309 // we need the available db retention for this call
1310 // so it has to be done last
1311 query_target_calculate_window(qt);
1312
1313 qt->timings.preprocessed_ut = now_monotonic_usec();
1314
1315 return qt;
1316 }
1317
1318 ssize_t weights_foreach_rrdmetric_in_context(RRDCONTEXT_ACQUIRED *rca,
1319 SIMPLE_PATTERN *scope_instances_sp,
1320 struct pattern_array *scope_labels_pa,
1321 SIMPLE_PATTERN *scope_dimensions_sp,
1322 SIMPLE_PATTERN *instances_sp,
1323 SIMPLE_PATTERN *chart_label_key_sp,
1324 struct pattern_array *labels_pa,
1325 SIMPLE_PATTERN *alerts_sp,
1326 SIMPLE_PATTERN *dimensions_sp,
1327 bool match_ids, bool match_names,
1328 size_t version,
1329 weights_add_metric_t cb,
1330 void *data
1331 ) {
1332 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
1333 if(!rc || rrd_flag_is_deleted(rc))
1334 return 0;
1335
1336 char host_node_id_str[UUID_STR_LEN] = "";
1337
1338 bool proceed = true;
1339
1340 ssize_t count = 0;
1341 RRDINSTANCE *ri;
1342 dfe_start_read(rc->rrdinstances, ri) {
1343 if(rrd_flag_is_deleted(ri))
1344 continue;
1345
1346 RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *) ri_dfe.item;
1347
1348 // Check scope_instances first - if it doesn't match, skip entirely
1349 if(scope_instances_sp) {
1350 QUERY_INSTANCE qi = { .ria = ria, };
1351 SIMPLE_PATTERN_RESULT ret = query_instance_matches(&qi, ri, scope_instances_sp, match_ids, match_names, version, host_node_id_str);
1352 query_instance_strings_free(&qi);
1353
1354 if (ret != SP_MATCHED_POSITIVE)
1355 continue;
1356 }
1357
1358 // Check scope_labels - if it doesn't match, skip entirely
1359 if(scope_labels_pa) {
1360 if(!query_instance_matches_labels(ri, NULL, scope_labels_pa))
1361 continue;
1362 }
1363
1364 if(instances_sp) {
1365 QUERY_INSTANCE qi = { .ria = ria, };
1366 SIMPLE_PATTERN_RESULT ret = query_instance_matches(&qi, ri, instances_sp, match_ids, match_names, version, host_node_id_str);
1367 query_instance_strings_free(&qi);
1368
1369 if (ret != SP_MATCHED_POSITIVE)
1370 continue;
1371 }
1372
1373 if(!query_instance_matches_labels(ri, chart_label_key_sp, labels_pa))
1374 continue;
1375
1376 if(alerts_sp && !query_target_match_alert_pattern(ria, alerts_sp))
1377 continue;
1378
1379 dfe_unlock(ri);
1380
1381 RRDMETRIC *rm;
1382 dfe_start_read(ri->rrdmetrics, rm) {
1383 if(rrd_flag_is_deleted(rm))
1384 continue;
1385
1386 // Check scope_dimensions first - if it doesn't match, skip entirely
1387 if(scope_dimensions_sp) {
1388 SIMPLE_PATTERN_RESULT ret = SP_NOT_MATCHED;
1389
1390 if (match_ids)
1391 ret = simple_pattern_matches_string_extract(scope_dimensions_sp, rm->id, NULL, 0);
1392
1393 if (ret == SP_NOT_MATCHED && match_names && (rm->name != rm->id || !match_ids))
1394 ret = simple_pattern_matches_string_extract(scope_dimensions_sp, rm->name, NULL, 0);
1395
1396 if(ret != SP_MATCHED_POSITIVE)
1397 continue;
1398 }
1399
1400 if(dimensions_sp) {
1401 SIMPLE_PATTERN_RESULT ret = SP_NOT_MATCHED;
1402
1403 if (match_ids)
1404 ret = simple_pattern_matches_string_extract(dimensions_sp, rm->id, NULL, 0);
1405
1406 if (ret == SP_NOT_MATCHED && match_names && (rm->name != rm->id || !match_ids))
1407 ret = simple_pattern_matches_string_extract(dimensions_sp, rm->name, NULL, 0);
1408
1409 if(ret != SP_MATCHED_POSITIVE)
1410 continue;
1411 }
1412
1413 dfe_unlock(rm);
1414
1415 RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)rm_dfe.item;
1416 ssize_t ret = cb(data, rc->rrdhost, rca, ria, rma);
1417
1418 if(ret < 0) {
1419 proceed = false;
1420 break;
1421 }
1422
1423 count += ret;
1424 }
1425 dfe_done(rm);
1426
1427 if(unlikely(!proceed))
1428 break;
1429 }
1430 dfe_done(ri);
1431
1432 return count;
1433 }