11
return false;
12
}
13
14
+bool query_target_aggregatable(struct query_target *qt) {
15
+ return (qt->request.options & RRDR_OPTION_RETURN_RAW);
16
+}
17
+
18
void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
19
rrdset2json(st, wb, NULL, NULL, 0);
20
}
24
case DATASOURCE_JSON:
25
return DATASOURCE_FORMAT_JSON;
26
27
+ case DATASOURCE_JSON2:
28
+ return DATASOURCE_FORMAT_JSON2;
29
+
30
case DATASOURCE_DATATABLE_JSON:
31
return DATASOURCE_FORMAT_DATATABLE_JSON;
32
181
if(!qt->group_by.used)
182
qt->request.group_by &= ~RRDR_GROUP_BY_LABEL;
183
177
- if(!(qt->request.group_by & (RRDR_GROUP_BY_NODE | RRDR_GROUP_BY_INSTANCE | RRDR_GROUP_BY_DIMENSION | RRDR_GROUP_BY_LABEL)))
184
+ if(!(qt->request.group_by & (RRDR_GROUP_BY_NODE | RRDR_GROUP_BY_INSTANCE | RRDR_GROUP_BY_DIMENSION | RRDR_GROUP_BY_LABEL | RRDR_GROUP_BY_SELECTED)))
185
qt->request.group_by = RRDR_GROUP_BY_DIMENSION;
186
187
int added = 0;
188
BUFFER *key = buffer_create(0, NULL);
189
QUERY_INSTANCE *last_qi = NULL;
190
size_t priority = 0;
184
- for(size_t c = 0; c < qt->query.used ;c++) {
185
- if(!rrdr_dimension_should_be_exposed(r->od[c], options))
191
+ time_t update_every_max = 0;
192
+ for(size_t d = 0; d < qt->query.used ; d++) {
193
+ if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED)))
194
continue;
195
188
- QUERY_METRIC *qm = query_metric(qt, c);
196
+ QUERY_METRIC *qm = query_metric(qt, d);
197
QUERY_INSTANCE *qi = query_instance(qt, qm->link.query_instance_id);
190
- QUERY_NODE *qn = query_node(qt, qm->link.query_host_id);
198
+ QUERY_NODE *qn = query_node(qt, qm->link.query_node_id);
199
200
if(qi != last_qi) {
201
priority = 0;
202
last_qi = qi;
203
+
204
+ time_t update_every = rrdinstance_acquired_update_every(qi->ria);
205
+ if(update_every > update_every_max)
206
+ update_every_max = update_every;
207
}
208
else
209
priority++;
210
211
+ // --------------------------------------------------------------------
212
// generate the group by key
213
214
buffer_flush(key);
202
- if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
203
- buffer_fast_strcat(key, "|", 1);
204
- buffer_strcat(key, query_metric_id(qt, qm));
215
+ if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
216
+ buffer_strcat(key, "__hidden_dimensions__");
217
}
206
- if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
207
- buffer_fast_strcat(key, "|", 1);
208
- buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
218
+ else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
219
+ buffer_strcat(key, "selected");
220
}
210
- if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
211
- DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
212
- for(size_t l = 0; l < qt->group_by.used ;l++) {
221
+ else {
222
+ if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
223
buffer_fast_strcat(key, "|", 1);
214
- rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
224
+ buffer_strcat(key, query_metric_id(qt, qm));
225
}
216
- }
217
- if(qt->request.group_by & RRDR_GROUP_BY_NODE) {
218
- buffer_fast_strcat(key, "|", 1);
219
- buffer_strcat(key, qn->rrdhost->machine_guid);
220
- }
226
222
- // append the units
223
- if(query_target_has_percentage_units(qt)) {
224
- buffer_fast_strcat(key, "|%", 2);
225
- }
226
- else {
227
- buffer_fast_strcat(key, "|", 1);
228
- buffer_strcat(key, rrdinstance_acquired_units(qi->ria));
227
+ if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
228
+ buffer_fast_strcat(key, "|", 1);
229
+ buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
230
+ }
231
+
232
+ if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
233
+ DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
234
+ for (size_t l = 0; l < qt->group_by.used; l++) {
235
+ buffer_fast_strcat(key, "|", 1);
236
+ rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
237
+ }
238
+ }
239
+
240
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
241
+ buffer_fast_strcat(key, "|", 1);
242
+ buffer_strcat(key, qn->rrdhost->machine_guid);
243
+ }
244
+
245
+ // append the units
246
+ if (query_target_has_percentage_units(qt)) {
247
+ buffer_fast_strcat(key, "|%", 2);
248
+ } else {
249
+ buffer_fast_strcat(key, "|", 1);
250
+ buffer_strcat(key, rrdinstance_acquired_units(qi->ria));
251
+ }
252
}
253
254
// lookup the key in the dictionary
260
261
*set = pos = added++;
262
263
+ // ----------------------------------------------------------------
264
// generate the dimension id
265
266
buffer_flush(key);
243
- if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
244
- buffer_strcat(key, query_metric_id(qt, qm));
267
+ if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
268
+ buffer_strcat(key, "__hidden_dimensions__");
269
}
246
- if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
247
- if(buffer_strlen(key) != 0)
248
- buffer_fast_strcat(key, ",", 1);
249
-
250
- if(qt->request.group_by & RRDR_GROUP_BY_NODE)
251
- buffer_strcat(key, rrdinstance_acquired_id(qi->ria));
252
- else
253
- buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
270
+ else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
271
+ buffer_strcat(key, "selected");
272
}
255
- if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
256
- DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
257
- for(size_t l = 0; l < qt->group_by.used ;l++) {
258
- if(buffer_strlen(key) != 0)
273
+ else {
274
+ if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
275
+ buffer_strcat(key, query_metric_id(qt, qm));
276
+ }
277
+
278
+ if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
279
+ if (buffer_strlen(key) != 0)
280
buffer_fast_strcat(key, ",", 1);
260
- rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
281
+
282
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE)
283
+ buffer_strcat(key, rrdinstance_acquired_id(qi->ria));
284
+ else
285
+ buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
286
}
262
- }
263
- if(qt->request.group_by & RRDR_GROUP_BY_NODE) {
264
- if(buffer_strlen(key) != 0)
265
- buffer_fast_strcat(key, ",", 1);
287
267
- buffer_strcat(key, qn->rrdhost->machine_guid);
288
+ if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
289
+ DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
290
+ for (size_t l = 0; l < qt->group_by.used; l++) {
291
+ if (buffer_strlen(key) != 0)
292
+ buffer_fast_strcat(key, ",", 1);
293
+ rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
294
+ }
295
+ }
296
+
297
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
298
+ if (buffer_strlen(key) != 0)
299
+ buffer_fast_strcat(key, ",", 1);
300
+
301
+ buffer_strcat(key, qn->rrdhost->machine_guid);
302
+ }
303
}
304
+
305
entries[pos].id = string_strdupz(buffer_tostring(key));
306
307
+ // ----------------------------------------------------------------
308
// generate the dimension name
309
310
buffer_flush(key);
274
- if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
275
- buffer_strcat(key, query_metric_name(qt, qm));
311
+ if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
312
+ buffer_strcat(key, "__hidden_dimensions__");
313
}
277
- if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
278
- if(buffer_strlen(key) != 0)
279
- buffer_fast_strcat(key, ",", 1);
280
-
281
- if(qt->request.group_by & RRDR_GROUP_BY_NODE)
282
- buffer_strcat(key, rrdinstance_acquired_name(qi->ria));
283
- else
284
- buffer_strcat(key, string2str(query_instance_name_fqdn(qt, qi)));
314
+ else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
315
+ buffer_strcat(key, "selected");
316
}
286
- if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
287
- DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
288
- for(size_t l = 0; l < qt->group_by.used ;l++) {
289
- if(buffer_strlen(key) != 0)
317
+ else {
318
+ if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
319
+ buffer_strcat(key, query_metric_name(qt, qm));
320
+ }
321
+
322
+ if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
323
+ if (buffer_strlen(key) != 0)
324
buffer_fast_strcat(key, ",", 1);
291
- rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
325
+
326
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE)
327
+ buffer_strcat(key, rrdinstance_acquired_name(qi->ria));
328
+ else
329
+ buffer_strcat(key, string2str(query_instance_name_fqdn(qt, qi)));
330
}
293
- }
294
- if(qt->request.group_by & RRDR_GROUP_BY_NODE) {
295
- if(buffer_strlen(key) != 0)
296
- buffer_fast_strcat(key, ",", 1);
331
298
- buffer_strcat(key, rrdhost_hostname(qn->rrdhost));
332
+ if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
333
+ DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
334
+ for (size_t l = 0; l < qt->group_by.used; l++) {
335
+ if (buffer_strlen(key) != 0)
336
+ buffer_fast_strcat(key, ",", 1);
337
+ rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
338
+ }
339
+ }
340
+
341
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
342
+ if (buffer_strlen(key) != 0)
343
+ buffer_fast_strcat(key, ",", 1);
344
+
345
+ buffer_strcat(key, rrdhost_hostname(qn->rrdhost));
346
+ }
347
}
348
+
349
entries[pos].name = string_strdupz(buffer_tostring(key));
350
351
// add the rest of the info
370
// copy the dimension flags decided by the query target
371
// we need this, because if a dimension is explicitly selected
372
// the query target adds to it the non-zero flag
324
- qm->status |= RRDR_DIMENSION_GROUPED | r->od[c];
325
- entries[pos].od |= RRDR_DIMENSION_GROUPED | r->od[c];
373
+ qm->status |= RRDR_DIMENSION_GROUPED | r->od[d];
374
+ entries[pos].od |= RRDR_DIMENSION_GROUPED | r->od[d];
375
}
376
377
// check if we have multiple units
411
r2->stats.result_points_generated = r2->d * r2->n;
412
413
// initialize r2 (dimension options, names, and ids)
365
- for(size_t c2 = 0; c2 < r2->d ; c2++) {
366
- r2->od[c2] = entries[c2].od;
367
- r2->di[c2] = entries[c2].id;
368
- r2->dn[c2] = entries[c2].name;
369
- r2->du[c2] = entries[c2].units;
370
- r2->dp[c2] = entries[c2].priority;
371
- r2->dgbc[c2] = entries[c2].count;
414
+ for(size_t d2 = 0; d2 < r2->d ; d2++) {
415
+ r2->od[d2] = entries[d2].od;
416
+ r2->di[d2] = entries[d2].id;
417
+ r2->dn[d2] = entries[d2].name;
418
+ r2->du[d2] = entries[d2].units;
419
+ r2->dp[d2] = entries[d2].priority;
420
+ r2->dgbc[d2] = entries[d2].count;
421
}
422
423
+ r2->partial_data_trimming.max_update_every = update_every_max;
424
+ r2->partial_data_trimming.expected_after =
425
+ (!(qt->request.options & RRDR_OPTION_RETURN_RAW) && qt->window.before >= qt->window.now - update_every_max) ?
426
+ qt->window.before - update_every_max :
427
+ qt->window.before;
428
+ r2->partial_data_trimming.trimmed_after = qt->window.before;
429
+
430
// initialize r2 (timestamps and value flags)
431
for(size_t i = 0; i != rows ;i++) {
432
// copy the timestamp
436
NETDATA_DOUBLE *cn2 = &r2->v[ i * r2->d ];
437
RRDR_VALUE_FLAGS *co2 = &r2->o[ i * r2->d ];
438
NETDATA_DOUBLE *ar2 = &r2->ar[ i * r2->d ];
383
- for (size_t c2 = 0; c2 < r2->d; c2++) {
384
- cn2[c2] = 0.0;
385
- ar2[c2] = 0.0;
386
- co2[c2] = RRDR_VALUE_EMPTY;
439
+ for (size_t d2 = 0; d2 < r2->d; d2++) {
440
+ cn2[d2] = 0.0;
441
+ ar2[d2] = 0.0;
442
+ co2[d2] = RRDR_VALUE_EMPTY;
443
}
444
}
445
446
// do the group_by
447
+ size_t last_row_gbc = 0;
448
for(size_t i = 0; i != rows ;i++) {
449
+
450
size_t idx = i * r->d;
451
NETDATA_DOUBLE *cn_base = &r->v[ idx ];
452
RRDR_VALUE_FLAGS *co_base = &r->o[ idx ];
458
NETDATA_DOUBLE *ar2_base = &r2->ar[ idx2 ];
459
uint32_t *gbc2_base = &r2->gbc[ idx2 ];
460
403
- for(size_t c = 0; c < r->d ;c++) {
404
- if (!rrdr_dimension_should_be_exposed(r->od[c], options))
461
+ size_t row_gbc = 0;
462
+ for(size_t d = 0; d < r->d ; d++) {
463
+ if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED)))
464
continue;
465
407
- NETDATA_DOUBLE n = cn_base[c];
408
- RRDR_VALUE_FLAGS o = co_base[c];
409
- NETDATA_DOUBLE ar = ar_base[c];
466
+ NETDATA_DOUBLE n = cn_base[d];
467
+ RRDR_VALUE_FLAGS o = co_base[d];
468
+ NETDATA_DOUBLE ar = ar_base[d];
469
470
if(o & RRDR_VALUE_EMPTY) {
471
if(options & RRDR_OPTION_NULL2ZERO)
477
if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
478
n = -n;
479
421
- QUERY_METRIC *qm = query_metric(qt, c);
422
- size_t c2 = qm->grouped_as.slot;
480
+ QUERY_METRIC *qm = query_metric(qt, d);
481
+ size_t d2 = qm->grouped_as.slot;
482
424
- NETDATA_DOUBLE *cn2 = &cn2_base[c2];
425
- RRDR_VALUE_FLAGS *co2 = &co2_base[c2];
426
- NETDATA_DOUBLE *ar2 = &ar2_base[c2];
427
- uint32_t *gbc2 = &gbc2_base[c2];
483
+ NETDATA_DOUBLE *cn2 = &cn2_base[d2];
484
+ RRDR_VALUE_FLAGS *co2 = &co2_base[d2];
485
+ NETDATA_DOUBLE *ar2 = &ar2_base[d2];
486
+ uint32_t *gbc2 = &gbc2_base[d2];
487
488
switch(qt->request.group_by_aggregate_function) {
489
default:
490
case RRDR_GROUP_BY_FUNCTION_AVERAGE:
491
case RRDR_GROUP_BY_FUNCTION_SUM:
433
- case RRDR_GROUP_BY_FUNCTION_SUM_COUNT:
492
*cn2 += n;
493
break;
494
506
*co2 |= (o & (RRDR_VALUE_RESET|RRDR_VALUE_PARTIAL));
507
*ar2 += ar;
508
(*gbc2)++;
509
+
510
+ row_gbc++;
511
}
512
+
513
+ if(unlikely(r->t[i] > r2->partial_data_trimming.expected_after && row_gbc < last_row_gbc)) {
514
+ // discard the rest of the points
515
+ r2->partial_data_trimming.trimmed_after = r->t[i];
516
+ r2->rows = i;
517
+ rows = i;
518
+ break;
519
+ }
520
+ else
521
+ last_row_gbc = row_gbc;
522
}
523
524
// apply averaging, remove RRDR_VALUE_EMPTY, find the non-zero dimensions, min and max
525
size_t min_max_values = 0;
526
NETDATA_DOUBLE min = NAN, max = NAN;
457
- for (size_t c2 = 0; c2 < r2->d; c2++) {
527
+ for (size_t d2 = 0; d2 < r2->d; d2++) {
528
size_t non_zero = 0;
529
530
for(size_t i = 0; i != rows ;i++) {
461
- size_t idx2 = i * r2->d + c2;
531
+ size_t idx2 = i * r2->d + d2;
532
533
NETDATA_DOUBLE *cn2 = &r2->v[ idx2 ];
534
RRDR_VALUE_FLAGS *co2 = &r2->o[ idx2 ];
538
if(likely(gbc2)) {
539
*co2 &= ~RRDR_VALUE_EMPTY;
540
471
- if(gbc2 != r2->dgbc[c2])
541
+ if(gbc2 != r2->dgbc[d2])
542
*co2 |= RRDR_VALUE_PARTIAL;
543
544
NETDATA_DOUBLE n;
545
476
- if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_SUM_COUNT) {
477
- n = *cn2 / gbc2;
478
- }
479
- else if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_AVERAGE) {
480
- *ar2 /= gbc2;
481
- n = *cn2 / gbc2;
482
- *cn2 = n;
483
- }
484
- else {
485
- *ar2 /= gbc2;
546
+ if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_AVERAGE)
547
+ n = (*cn2 /= gbc2);
548
+ else
549
n = *cn2;
487
- }
550
+
551
+ if(!query_target_aggregatable(qt))
552
+ *ar2 /= gbc2;
553
554
if(islessgreater(n, 0.0))
555
non_zero++;
569
}
570
571
if(non_zero)
507
- r2->od[c2] |= RRDR_DIMENSION_NONZERO;
572
+ r2->od[d2] |= RRDR_DIMENSION_NONZERO;
573
}
574
575
r2->view.min = min;
579
buffer_free(key);
580
581
if(!r2 && entries && added) {
517
- for(long c = 0; c < added ;c++) {
518
- string_freez(entries[c].id);
519
- string_freez(entries[c].name);
582
+ for(int d2 = 0; d2 < added ; d2++) {
583
+ string_freez(entries[d2].id);
584
+ string_freez(entries[d2].name);
585
}
586
}
587
onewayalloc_freez(r->internal.owa, entries);
824
rrdr2json(r, wb, options, 0);
825
826
if(options & RRDR_OPTION_JSON_WRAP) {
762
- if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_SUM_COUNT) {
827
+ if (query_target_aggregatable(qt)) {
828
buffer_json_member_add_key_only(wb, "group_by_count");
829
rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_GBC, false);
830
}
766
- if(options & RRDR_OPTION_RETURN_JWAR) {
831
+ if (options & RRDR_OPTION_RETURN_JWAR) {
832
buffer_json_member_add_key_only(wb, "anomaly_rates");
833
rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_AR, false);
834
}
770
- if(options & RRDR_OPTION_JW_ANNOTATIONS) {
771
- rrdr_json_wrapper_annotations(r, wb, format, options);
772
- }
835
wrapper_end(r, wb, format, options);
836
}
837
break;
838
+
839
+ case DATASOURCE_JSON2:
840
+ wb->content_type = CT_APPLICATION_JSON;
841
+ wrapper_begin(r, wb, format, options, group_method);
842
+ rrdr2json_v2(r, wb, format, options);
843
+ wrapper_end(r, wb, format, options);
844
+ break;
845
}
846
847
if(r != r1)