query.c 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "query.h"
  3. #include "web/api/formatters/rrd2json.h"
  4. #include "rrdr.h"
  5. #include "average/average.h"
  6. #include "incremental_sum/incremental_sum.h"
  7. #include "max/max.h"
  8. #include "median/median.h"
  9. #include "min/min.h"
  10. #include "sum/sum.h"
  11. #include "stddev/stddev.h"
  12. #include "ses/ses.h"
  13. #include "des/des.h"
  14. // ----------------------------------------------------------------------------
  15. static struct {
  16. const char *name;
  17. uint32_t hash;
  18. RRDR_GROUPING value;
  19. // One time initialization for the module.
  20. // This is called once, when netdata starts.
  21. void (*init)(void);
  22. // Allocate all required structures for a query.
  23. // This is called once for each netdata query.
  24. void *(*create)(struct rrdresult *r);
  25. // Cleanup collected values, but don't destroy the structures.
  26. // This is called when the query engine switches dimensions,
  27. // as part of the same query (so same chart, switching metric).
  28. void (*reset)(struct rrdresult *r);
  29. // Free all resources allocated for the query.
  30. void (*free)(struct rrdresult *r);
  31. // Add a single value into the calculation.
  32. // The module may decide to cache it, or use it in the fly.
  33. void (*add)(struct rrdresult *r, calculated_number value);
  34. // Generate a single result for the values added so far.
  35. // More values and points may be requested later.
  36. // It is up to the module to reset its internal structures
  37. // when flushing it (so for a few modules it may be better to
  38. // continue after a flush as if nothing changed, for others a
  39. // cleanup of the internal structures may be required).
  40. calculated_number (*flush)(struct rrdresult *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
  41. } api_v1_data_groups[] = {
  42. {.name = "average",
  43. .hash = 0,
  44. .value = RRDR_GROUPING_AVERAGE,
  45. .init = NULL,
  46. .create= grouping_create_average,
  47. .reset = grouping_reset_average,
  48. .free = grouping_free_average,
  49. .add = grouping_add_average,
  50. .flush = grouping_flush_average
  51. },
  52. {.name = "mean", // alias on 'average'
  53. .hash = 0,
  54. .value = RRDR_GROUPING_AVERAGE,
  55. .init = NULL,
  56. .create= grouping_create_average,
  57. .reset = grouping_reset_average,
  58. .free = grouping_free_average,
  59. .add = grouping_add_average,
  60. .flush = grouping_flush_average
  61. },
  62. {.name = "incremental_sum",
  63. .hash = 0,
  64. .value = RRDR_GROUPING_INCREMENTAL_SUM,
  65. .init = NULL,
  66. .create= grouping_create_incremental_sum,
  67. .reset = grouping_reset_incremental_sum,
  68. .free = grouping_free_incremental_sum,
  69. .add = grouping_add_incremental_sum,
  70. .flush = grouping_flush_incremental_sum
  71. },
  72. {.name = "incremental-sum",
  73. .hash = 0,
  74. .value = RRDR_GROUPING_INCREMENTAL_SUM,
  75. .init = NULL,
  76. .create= grouping_create_incremental_sum,
  77. .reset = grouping_reset_incremental_sum,
  78. .free = grouping_free_incremental_sum,
  79. .add = grouping_add_incremental_sum,
  80. .flush = grouping_flush_incremental_sum
  81. },
  82. {.name = "median",
  83. .hash = 0,
  84. .value = RRDR_GROUPING_MEDIAN,
  85. .init = NULL,
  86. .create= grouping_create_median,
  87. .reset = grouping_reset_median,
  88. .free = grouping_free_median,
  89. .add = grouping_add_median,
  90. .flush = grouping_flush_median
  91. },
  92. {.name = "min",
  93. .hash = 0,
  94. .value = RRDR_GROUPING_MIN,
  95. .init = NULL,
  96. .create= grouping_create_min,
  97. .reset = grouping_reset_min,
  98. .free = grouping_free_min,
  99. .add = grouping_add_min,
  100. .flush = grouping_flush_min
  101. },
  102. {.name = "max",
  103. .hash = 0,
  104. .value = RRDR_GROUPING_MAX,
  105. .init = NULL,
  106. .create= grouping_create_max,
  107. .reset = grouping_reset_max,
  108. .free = grouping_free_max,
  109. .add = grouping_add_max,
  110. .flush = grouping_flush_max
  111. },
  112. {.name = "sum",
  113. .hash = 0,
  114. .value = RRDR_GROUPING_SUM,
  115. .init = NULL,
  116. .create= grouping_create_sum,
  117. .reset = grouping_reset_sum,
  118. .free = grouping_free_sum,
  119. .add = grouping_add_sum,
  120. .flush = grouping_flush_sum
  121. },
  122. // standard deviation
  123. {.name = "stddev",
  124. .hash = 0,
  125. .value = RRDR_GROUPING_STDDEV,
  126. .init = NULL,
  127. .create= grouping_create_stddev,
  128. .reset = grouping_reset_stddev,
  129. .free = grouping_free_stddev,
  130. .add = grouping_add_stddev,
  131. .flush = grouping_flush_stddev
  132. },
  133. {.name = "cv", // coefficient variation is calculated by stddev
  134. .hash = 0,
  135. .value = RRDR_GROUPING_CV,
  136. .init = NULL,
  137. .create= grouping_create_stddev, // not an error, stddev calculates this too
  138. .reset = grouping_reset_stddev, // not an error, stddev calculates this too
  139. .free = grouping_free_stddev, // not an error, stddev calculates this too
  140. .add = grouping_add_stddev, // not an error, stddev calculates this too
  141. .flush = grouping_flush_coefficient_of_variation
  142. },
  143. {.name = "rsd", // alias of 'cv'
  144. .hash = 0,
  145. .value = RRDR_GROUPING_CV,
  146. .init = NULL,
  147. .create= grouping_create_stddev, // not an error, stddev calculates this too
  148. .reset = grouping_reset_stddev, // not an error, stddev calculates this too
  149. .free = grouping_free_stddev, // not an error, stddev calculates this too
  150. .add = grouping_add_stddev, // not an error, stddev calculates this too
  151. .flush = grouping_flush_coefficient_of_variation
  152. },
  153. /*
  154. {.name = "mean", // same as average, no need to define it again
  155. .hash = 0,
  156. .value = RRDR_GROUPING_MEAN,
  157. .setup = NULL,
  158. .create= grouping_create_stddev,
  159. .reset = grouping_reset_stddev,
  160. .free = grouping_free_stddev,
  161. .add = grouping_add_stddev,
  162. .flush = grouping_flush_mean
  163. },
  164. */
  165. /*
  166. {.name = "variance", // meaningless to offer
  167. .hash = 0,
  168. .value = RRDR_GROUPING_VARIANCE,
  169. .setup = NULL,
  170. .create= grouping_create_stddev,
  171. .reset = grouping_reset_stddev,
  172. .free = grouping_free_stddev,
  173. .add = grouping_add_stddev,
  174. .flush = grouping_flush_variance
  175. },
  176. */
  177. // single exponential smoothing
  178. {.name = "ses",
  179. .hash = 0,
  180. .value = RRDR_GROUPING_SES,
  181. .init = grouping_init_ses,
  182. .create= grouping_create_ses,
  183. .reset = grouping_reset_ses,
  184. .free = grouping_free_ses,
  185. .add = grouping_add_ses,
  186. .flush = grouping_flush_ses
  187. },
  188. {.name = "ema", // alias for 'ses'
  189. .hash = 0,
  190. .value = RRDR_GROUPING_SES,
  191. .init = NULL,
  192. .create= grouping_create_ses,
  193. .reset = grouping_reset_ses,
  194. .free = grouping_free_ses,
  195. .add = grouping_add_ses,
  196. .flush = grouping_flush_ses
  197. },
  198. {.name = "ewma", // alias for ses
  199. .hash = 0,
  200. .value = RRDR_GROUPING_SES,
  201. .init = NULL,
  202. .create= grouping_create_ses,
  203. .reset = grouping_reset_ses,
  204. .free = grouping_free_ses,
  205. .add = grouping_add_ses,
  206. .flush = grouping_flush_ses
  207. },
  208. // double exponential smoothing
  209. {.name = "des",
  210. .hash = 0,
  211. .value = RRDR_GROUPING_DES,
  212. .init = grouping_init_des,
  213. .create= grouping_create_des,
  214. .reset = grouping_reset_des,
  215. .free = grouping_free_des,
  216. .add = grouping_add_des,
  217. .flush = grouping_flush_des
  218. },
  219. // terminator
  220. {.name = NULL,
  221. .hash = 0,
  222. .value = RRDR_GROUPING_UNDEFINED,
  223. .init = NULL,
  224. .create= grouping_create_average,
  225. .reset = grouping_reset_average,
  226. .free = grouping_free_average,
  227. .add = grouping_add_average,
  228. .flush = grouping_flush_average
  229. }
  230. };
  231. void web_client_api_v1_init_grouping(void) {
  232. int i;
  233. for(i = 0; api_v1_data_groups[i].name ; i++) {
  234. api_v1_data_groups[i].hash = simple_hash(api_v1_data_groups[i].name);
  235. if(api_v1_data_groups[i].init)
  236. api_v1_data_groups[i].init();
  237. }
  238. }
  239. const char *group_method2string(RRDR_GROUPING group) {
  240. int i;
  241. for(i = 0; api_v1_data_groups[i].name ; i++) {
  242. if(api_v1_data_groups[i].value == group) {
  243. return api_v1_data_groups[i].name;
  244. }
  245. }
  246. return "unknown-group-method";
  247. }
  248. RRDR_GROUPING web_client_api_request_v1_data_group(const char *name, RRDR_GROUPING def) {
  249. int i;
  250. uint32_t hash = simple_hash(name);
  251. for(i = 0; api_v1_data_groups[i].name ; i++)
  252. if(unlikely(hash == api_v1_data_groups[i].hash && !strcmp(name, api_v1_data_groups[i].name)))
  253. return api_v1_data_groups[i].value;
  254. return def;
  255. }
  256. // ----------------------------------------------------------------------------
  257. static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options, const char *dims) {
  258. rrdset_check_rdlock(r->st);
  259. if(unlikely(!dims || !*dims || (dims[0] == '*' && dims[1] == '\0'))) return;
  260. int match_ids = 0, match_names = 0;
  261. if(unlikely(options & RRDR_OPTION_MATCH_IDS))
  262. match_ids = 1;
  263. if(unlikely(options & RRDR_OPTION_MATCH_NAMES))
  264. match_names = 1;
  265. if(likely(!match_ids && !match_names))
  266. match_ids = match_names = 1;
  267. SIMPLE_PATTERN *pattern = simple_pattern_create(dims, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);
  268. RRDDIM *d;
  269. long c, dims_selected = 0, dims_not_hidden_not_zero = 0;
  270. for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
  271. if( (match_ids && simple_pattern_matches(pattern, d->id))
  272. || (match_names && simple_pattern_matches(pattern, d->name))
  273. ) {
  274. r->od[c] |= RRDR_DIMENSION_SELECTED;
  275. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) r->od[c] &= ~RRDR_DIMENSION_HIDDEN;
  276. dims_selected++;
  277. // since the user needs this dimension
  278. // make it appear as NONZERO, to return it
  279. // even if the dimension has only zeros
  280. // unless option non_zero is set
  281. if(unlikely(!(options & RRDR_OPTION_NONZERO)))
  282. r->od[c] |= RRDR_DIMENSION_NONZERO;
  283. // count the visible dimensions
  284. if(likely(r->od[c] & RRDR_DIMENSION_NONZERO))
  285. dims_not_hidden_not_zero++;
  286. }
  287. else {
  288. r->od[c] |= RRDR_DIMENSION_HIDDEN;
  289. if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED;
  290. }
  291. }
  292. simple_pattern_free(pattern);
  293. // check if all dimensions are hidden
  294. if(unlikely(!dims_not_hidden_not_zero && dims_selected)) {
  295. // there are a few selected dimensions
  296. // but they are all zero
  297. // enable the selected ones
  298. // to avoid returning an empty chart
  299. for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
  300. if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED))
  301. r->od[c] |= RRDR_DIMENSION_NONZERO;
  302. }
  303. }
  304. // ----------------------------------------------------------------------------
  305. // helpers to find our way in RRDR
  306. static inline RRDR_VALUE_FLAGS *rrdr_line_options(RRDR *r, long rrdr_line) {
  307. return &r->o[ rrdr_line * r->d ];
  308. }
  309. static inline calculated_number *rrdr_line_values(RRDR *r, long rrdr_line) {
  310. return &r->v[ rrdr_line * r->d ];
  311. }
  312. static inline long rrdr_line_init(RRDR *r, time_t t, long rrdr_line) {
  313. rrdr_line++;
  314. #ifdef NETDATA_INTERNAL_CHECKS
  315. if(unlikely(rrdr_line >= r->n))
  316. error("INTERNAL ERROR: requested to step above RRDR size for chart '%s'", r->st->name);
  317. if(unlikely(r->t[rrdr_line] != 0 && r->t[rrdr_line] != t))
  318. error("INTERNAL ERROR: overwriting the timestamp of RRDR line %zu from %zu to %zu, of chart '%s'", (size_t)rrdr_line, (size_t)r->t[rrdr_line], (size_t)t, r->st->name);
  319. #endif
  320. // save the time
  321. r->t[rrdr_line] = t;
  322. return rrdr_line;
  323. }
  324. static inline void rrdr_done(RRDR *r, long rrdr_line) {
  325. r->rows = rrdr_line + 1;
  326. }
  327. // ----------------------------------------------------------------------------
  328. // fill RRDR for a single dimension
  329. static inline void do_dimension(
  330. RRDR *r
  331. , long points_wanted
  332. , RRDDIM *rd
  333. , long dim_id_in_rrdr
  334. , time_t after_wanted
  335. , time_t before_wanted
  336. ){
  337. RRDSET *st = r->st;
  338. time_t
  339. now = after_wanted,
  340. dt = st->update_every,
  341. max_date = 0,
  342. min_date = 0;
  343. long
  344. group_size = r->group,
  345. points_added = 0,
  346. values_in_group = 0,
  347. values_in_group_non_zero = 0,
  348. rrdr_line = -1;
  349. RRDR_VALUE_FLAGS
  350. group_value_flags = RRDR_VALUE_NOTHING;
  351. struct rrddim_query_handle handle;
  352. uint8_t initialized_query;
  353. calculated_number min = r->min, max = r->max;
  354. size_t db_points_read = 0;
  355. for(initialized_query = 0 ; points_added < points_wanted ; now += dt) {
  356. // make sure we return data in the proper time range
  357. if(unlikely(now > before_wanted)) {
  358. #ifdef NETDATA_INTERNAL_CHECKS
  359. r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
  360. #endif
  361. break;
  362. }
  363. if(unlikely(now < after_wanted)) {
  364. #ifdef NETDATA_INTERNAL_CHECKS
  365. r->internal.log = "skipped, because attempted to access the db before 'wanted after'";
  366. #endif
  367. continue;
  368. }
  369. if (unlikely(!initialized_query)) {
  370. rd->state->query_ops.init(rd, &handle, now, before_wanted);
  371. initialized_query = 1;
  372. }
  373. // read the value from the database
  374. //storage_number n = rd->values[slot];
  375. #ifdef NETDATA_INTERNAL_CHECKS
  376. if (rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
  377. #ifdef ENABLE_DBENGINE
  378. if (now != handle.rrdeng.now)
  379. error("INTERNAL CHECK: Unaligned query for %s, database time: %ld, expected time: %ld", rd->id, (long)handle.rrdeng.now, (long)now);
  380. #endif
  381. } else if (rrdset_time2slot(st, now) != (long unsigned)handle.slotted.slot) {
  382. error("INTERNAL CHECK: Unaligned query for %s, database slot: %lu, expected slot: %lu", rd->id, (long unsigned)handle.slotted.slot, rrdset_time2slot(st, now));
  383. }
  384. #endif
  385. storage_number n = rd->state->query_ops.next_metric(&handle);
  386. calculated_number value = NAN;
  387. if(likely(does_storage_number_exist(n))) {
  388. value = unpack_storage_number(n);
  389. if(likely(value != 0.0))
  390. values_in_group_non_zero++;
  391. if(unlikely(did_storage_number_reset(n)))
  392. group_value_flags |= RRDR_VALUE_RESET;
  393. }
  394. // add this value for grouping
  395. r->internal.grouping_add(r, value);
  396. values_in_group++;
  397. db_points_read++;
  398. if(unlikely(values_in_group == group_size)) {
  399. rrdr_line = rrdr_line_init(r, now, rrdr_line);
  400. if(unlikely(!min_date)) min_date = now;
  401. max_date = now;
  402. // find the place to store our values
  403. RRDR_VALUE_FLAGS *rrdr_value_options_ptr = &r->o[rrdr_line * r->d + dim_id_in_rrdr];
  404. // update the dimension options
  405. if(likely(values_in_group_non_zero))
  406. r->od[dim_id_in_rrdr] |= RRDR_DIMENSION_NONZERO;
  407. // store the specific point options
  408. *rrdr_value_options_ptr = group_value_flags;
  409. // store the value
  410. calculated_number value = r->internal.grouping_flush(r, rrdr_value_options_ptr);
  411. r->v[rrdr_line * r->d + dim_id_in_rrdr] = value;
  412. if(likely(points_added || dim_id_in_rrdr)) {
  413. // find the min/max across all dimensions
  414. if(unlikely(value < min)) min = value;
  415. if(unlikely(value > max)) max = value;
  416. }
  417. else {
  418. // runs only when dim_id_in_rrdr == 0 && points_added == 0
  419. // so, on the first point added for the query.
  420. min = max = value;
  421. }
  422. points_added++;
  423. values_in_group = 0;
  424. group_value_flags = RRDR_VALUE_NOTHING;
  425. values_in_group_non_zero = 0;
  426. }
  427. }
  428. if (likely(initialized_query))
  429. rd->state->query_ops.finalize(&handle);
  430. r->internal.db_points_read += db_points_read;
  431. r->internal.result_points_generated += points_added;
  432. r->min = min;
  433. r->max = max;
  434. r->before = max_date;
  435. r->after = min_date - (r->group - 1) * r->st->update_every;
  436. rrdr_done(r, rrdr_line);
  437. #ifdef NETDATA_INTERNAL_CHECKS
  438. if(unlikely(r->rows != points_added))
  439. error("INTERNAL ERROR: %s.%s added %zu rows, but RRDR says I added %zu.", r->st->name, rd->name, (size_t)points_added, (size_t)r->rows);
  440. #endif
  441. }
  442. // ----------------------------------------------------------------------------
  443. // fill RRDR for the whole chart
  444. #ifdef NETDATA_INTERNAL_CHECKS
  445. static void rrd2rrdr_log_request_response_metdata(RRDR *r
  446. , RRDR_GROUPING group_method
  447. , int aligned
  448. , long group
  449. , long resampling_time
  450. , long resampling_group
  451. , time_t after_wanted
  452. , time_t after_requested
  453. , time_t before_wanted
  454. , time_t before_requested
  455. , long points_requested
  456. , long points_wanted
  457. //, size_t after_slot
  458. //, size_t before_slot
  459. , const char *msg
  460. ) {
  461. info("INTERNAL ERROR: rrd2rrdr() on %s update every %d with %s grouping %s (group: %ld, resampling_time: %ld, resampling_group: %ld), "
  462. "after (got: %zu, want: %zu, req: %zu, db: %zu), "
  463. "before (got: %zu, want: %zu, req: %zu, db: %zu), "
  464. "duration (got: %zu, want: %zu, req: %zu, db: %zu), "
  465. //"slot (after: %zu, before: %zu, delta: %zu), "
  466. "points (got: %ld, want: %ld, req: %ld, db: %ld), "
  467. "%s"
  468. , r->st->name
  469. , r->st->update_every
  470. // grouping
  471. , (aligned) ? "aligned" : "unaligned"
  472. , group_method2string(group_method)
  473. , group
  474. , resampling_time
  475. , resampling_group
  476. // after
  477. , (size_t)r->after
  478. , (size_t)after_wanted
  479. , (size_t)after_requested
  480. , (size_t)rrdset_first_entry_t(r->st)
  481. // before
  482. , (size_t)r->before
  483. , (size_t)before_wanted
  484. , (size_t)before_requested
  485. , (size_t)rrdset_last_entry_t(r->st)
  486. // duration
  487. , (size_t)(r->before - r->after + r->st->update_every)
  488. , (size_t)(before_wanted - after_wanted + r->st->update_every)
  489. , (size_t)(before_requested - after_requested)
  490. , (size_t)((rrdset_last_entry_t(r->st) - rrdset_first_entry_t(r->st)) + r->st->update_every)
  491. // slot
  492. /*
  493. , after_slot
  494. , before_slot
  495. , (after_slot > before_slot) ? (r->st->entries - after_slot + before_slot) : (before_slot - after_slot)
  496. */
  497. // points
  498. , r->rows
  499. , points_wanted
  500. , points_requested
  501. , r->st->entries
  502. // message
  503. , msg
  504. );
  505. }
  506. #endif // NETDATA_INTERNAL_CHECKS
  507. RRDR *rrd2rrdr(
  508. RRDSET *st
  509. , long points_requested
  510. , long long after_requested
  511. , long long before_requested
  512. , RRDR_GROUPING group_method
  513. , long resampling_time_requested
  514. , RRDR_OPTIONS options
  515. , const char *dimensions
  516. ) {
  517. int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
  518. int absolute_period_requested = -1;
  519. time_t first_entry_t = rrdset_first_entry_t(st);
  520. time_t last_entry_t = rrdset_last_entry_t(st);
  521. if(before_requested == 0 && after_requested == 0) {
  522. // dump the all the data
  523. before_requested = last_entry_t;
  524. after_requested = first_entry_t;
  525. absolute_period_requested = 0;
  526. }
  527. // allow relative for before (smaller than API_RELATIVE_TIME_MAX)
  528. if(((before_requested < 0)?-before_requested:before_requested) <= API_RELATIVE_TIME_MAX) {
  529. if(abs(before_requested) % st->update_every) {
  530. // make sure it is multiple of st->update_every
  531. if(before_requested < 0) before_requested = before_requested - st->update_every - before_requested % st->update_every;
  532. else before_requested = before_requested + st->update_every - before_requested % st->update_every;
  533. }
  534. if(before_requested > 0) before_requested = first_entry_t + before_requested;
  535. else before_requested = last_entry_t + before_requested;
  536. absolute_period_requested = 0;
  537. }
  538. // allow relative for after (smaller than API_RELATIVE_TIME_MAX)
  539. if(((after_requested < 0)?-after_requested:after_requested) <= API_RELATIVE_TIME_MAX) {
  540. if(after_requested == 0) after_requested = -st->update_every;
  541. if(abs(after_requested) % st->update_every) {
  542. // make sure it is multiple of st->update_every
  543. if(after_requested < 0) after_requested = after_requested - st->update_every - after_requested % st->update_every;
  544. else after_requested = after_requested + st->update_every - after_requested % st->update_every;
  545. }
  546. after_requested = before_requested + after_requested;
  547. absolute_period_requested = 0;
  548. }
  549. if(absolute_period_requested == -1)
  550. absolute_period_requested = 1;
  551. // make sure they are within our timeframe
  552. if(before_requested > last_entry_t) before_requested = last_entry_t;
  553. if(before_requested < first_entry_t) before_requested = first_entry_t;
  554. if(after_requested > last_entry_t) after_requested = last_entry_t;
  555. if(after_requested < first_entry_t) after_requested = first_entry_t;
  556. // check if they are reversed
  557. if(after_requested > before_requested) {
  558. time_t tmp = before_requested;
  559. before_requested = after_requested;
  560. after_requested = tmp;
  561. }
  562. // the duration of the chart
  563. time_t duration = before_requested - after_requested;
  564. long available_points = duration / st->update_every;
  565. if(duration <= 0 || available_points <= 0)
  566. return rrdr_create(st, 1);
  567. // check the number of wanted points in the result
  568. if(unlikely(points_requested < 0)) points_requested = -points_requested;
  569. if(unlikely(points_requested > available_points)) points_requested = available_points;
  570. if(unlikely(points_requested == 0)) points_requested = available_points;
  571. // calculate the desired grouping of source data points
  572. long group = available_points / points_requested;
  573. if(unlikely(group <= 0)) group = 1;
  574. if(unlikely(available_points % points_requested > points_requested / 2)) group++; // rounding to the closest integer
  575. // resampling_time_requested enforces a certain grouping multiple
  576. calculated_number resampling_divisor = 1.0;
  577. long resampling_group = 1;
  578. if(unlikely(resampling_time_requested > st->update_every)) {
  579. if (unlikely(resampling_time_requested > duration)) {
  580. // group_time is above the available duration
  581. #ifdef NETDATA_INTERNAL_CHECKS
  582. info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, resampling_time_requested, duration);
  583. #endif
  584. after_requested = before_requested - resampling_time_requested;
  585. duration = before_requested - after_requested;
  586. available_points = duration / st->update_every;
  587. group = available_points / points_requested;
  588. }
  589. // if the duration is not aligned to resampling time
  590. // extend the duration to the past, to avoid a gap at the chart
  591. // only when the missing duration is above 1/10th of a point
  592. if(duration % resampling_time_requested) {
  593. time_t delta = duration % resampling_time_requested;
  594. if(delta > resampling_time_requested / 10) {
  595. after_requested -= resampling_time_requested - delta;
  596. duration = before_requested - after_requested;
  597. available_points = duration / st->update_every;
  598. group = available_points / points_requested;
  599. }
  600. }
  601. // the points we should group to satisfy gtime
  602. resampling_group = resampling_time_requested / st->update_every;
  603. if(unlikely(resampling_time_requested % st->update_every)) {
  604. #ifdef NETDATA_INTERNAL_CHECKS
  605. info("INTERNAL CHECK: %s: requested gtime %ld secs, is not a multiple of the chart's data collection frequency %d secs", st->id, resampling_time_requested, st->update_every);
  606. #endif
  607. resampling_group++;
  608. }
  609. // adapt group according to resampling_group
  610. if(unlikely(group < resampling_group)) group = resampling_group; // do not allow grouping below the desired one
  611. if(unlikely(group % resampling_group)) group += resampling_group - (group % resampling_group); // make sure group is multiple of resampling_group
  612. //resampling_divisor = group / resampling_group;
  613. resampling_divisor = (calculated_number)(group * st->update_every) / (calculated_number)resampling_time_requested;
  614. }
  615. // now that we have group,
  616. // align the requested timeframe to fit it.
  617. if(aligned) {
  618. // alignement has been requested, so align the values
  619. before_requested -= (before_requested % group);
  620. after_requested -= (after_requested % group);
  621. }
  622. // we align the request on requested_before
  623. time_t before_wanted = before_requested;
  624. if(likely(before_wanted > last_entry_t)) {
  625. #ifdef NETDATA_INTERNAL_CHECKS
  626. error("INTERNAL ERROR: rrd2rrdr() on %s, before_wanted is after db max", st->name);
  627. #endif
  628. before_wanted = last_entry_t - (last_entry_t % ( ((aligned)?group:1) * st->update_every ));
  629. }
  630. //size_t before_slot = rrdset_time2slot(st, before_wanted);
  631. // we need to estimate the number of points, for having
  632. // an integer number of values per point
  633. long points_wanted = (before_wanted - after_requested) / (st->update_every * group);
  634. time_t after_wanted = before_wanted - (points_wanted * group * st->update_every) + st->update_every;
  635. if(unlikely(after_wanted < first_entry_t)) {
  636. // hm... we go to the past, calculate again points_wanted using all the db from before_wanted to the beginning
  637. points_wanted = (before_wanted - first_entry_t) / group;
  638. // recalculate after wanted with the new number of points
  639. after_wanted = before_wanted - (points_wanted * group * st->update_every) + st->update_every;
  640. if(unlikely(after_wanted < first_entry_t)) {
  641. #ifdef NETDATA_INTERNAL_CHECKS
  642. error("INTERNAL ERROR: rrd2rrdr() on %s, after_wanted is before db min", st->name);
  643. #endif
  644. after_wanted = first_entry_t - (first_entry_t % ( ((aligned)?group:1) * st->update_every )) + ( ((aligned)?group:1) * st->update_every );
  645. }
  646. }
  647. //size_t after_slot = rrdset_time2slot(st, after_wanted);
  648. // check if they are reversed
  649. if(unlikely(after_wanted > before_wanted)) {
  650. #ifdef NETDATA_INTERNAL_CHECKS
  651. error("INTERNAL ERROR: rrd2rrdr() on %s, reversed wanted after/before", st->name);
  652. #endif
  653. time_t tmp = before_wanted;
  654. before_wanted = after_wanted;
  655. after_wanted = tmp;
  656. }
  657. // recalculate points_wanted using the final time-frame
  658. points_wanted = (before_wanted - after_wanted) / st->update_every / group + 1;
  659. if(unlikely(points_wanted < 0)) {
  660. #ifdef NETDATA_INTERNAL_CHECKS
  661. error("INTERNAL ERROR: rrd2rrdr() on %s, points_wanted is %ld", st->name, points_wanted);
  662. #endif
  663. points_wanted = 0;
  664. }
  665. #ifdef NETDATA_INTERNAL_CHECKS
  666. duration = before_wanted - after_wanted;
  667. if(after_wanted < first_entry_t)
  668. error("INTERNAL CHECK: after_wanted %u is too small, minimum %u", (uint32_t)after_wanted, (uint32_t)first_entry_t);
  669. if(after_wanted > last_entry_t)
  670. error("INTERNAL CHECK: after_wanted %u is too big, maximum %u", (uint32_t)after_wanted, (uint32_t)last_entry_t);
  671. if(before_wanted < first_entry_t)
  672. error("INTERNAL CHECK: before_wanted %u is too small, minimum %u", (uint32_t)before_wanted, (uint32_t)first_entry_t);
  673. if(before_wanted > last_entry_t)
  674. error("INTERNAL CHECK: before_wanted %u is too big, maximum %u", (uint32_t)before_wanted, (uint32_t)last_entry_t);
  675. /*
  676. if(before_slot >= (size_t)st->entries)
  677. error("INTERNAL CHECK: before_slot is invalid %zu, expected 0 to %ld", before_slot, st->entries - 1);
  678. if(after_slot >= (size_t)st->entries)
  679. error("INTERNAL CHECK: after_slot is invalid %zu, expected 0 to %ld", after_slot, st->entries - 1);
  680. */
  681. if(points_wanted > (before_wanted - after_wanted) / group / st->update_every + 1)
  682. error("INTERNAL CHECK: points_wanted %ld is more than points %ld", points_wanted, (before_wanted - after_wanted) / group / st->update_every + 1);
  683. if(group < resampling_group)
  684. error("INTERNAL CHECK: group %ld is less than the desired group points %ld", group, resampling_group);
  685. if(group > resampling_group && group % resampling_group)
  686. error("INTERNAL CHECK: group %ld is not a multiple of the desired group points %ld", group, resampling_group);
  687. #endif
  688. // -------------------------------------------------------------------------
  689. // initialize our result set
  690. // this also locks the chart for us
  691. RRDR *r = rrdr_create(st, points_wanted);
  692. if(unlikely(!r)) {
  693. #ifdef NETDATA_INTERNAL_CHECKS
  694. error("INTERNAL CHECK: Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (uint32_t)duration, points_wanted);
  695. #endif
  696. return NULL;
  697. }
  698. if(unlikely(!r->d || !points_wanted)) {
  699. #ifdef NETDATA_INTERNAL_CHECKS
  700. error("INTERNAL CHECK: Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%zu, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (size_t)duration, points_wanted);
  701. #endif
  702. return r;
  703. }
  704. if(unlikely(absolute_period_requested == 1))
  705. r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
  706. else
  707. r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
  708. // find how many dimensions we have
  709. long dimensions_count = r->d;
  710. // -------------------------------------------------------------------------
  711. // initialize RRDR
  712. r->group = group;
  713. r->update_every = (int)group * st->update_every;
  714. r->before = before_wanted;
  715. r->after = after_wanted;
  716. r->internal.points_wanted = points_wanted;
  717. r->internal.resampling_group = resampling_group;
  718. r->internal.resampling_divisor = resampling_divisor;
  719. // -------------------------------------------------------------------------
  720. // assign the processor functions
  721. {
  722. int i, found = 0;
  723. for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
  724. if(api_v1_data_groups[i].value == group_method) {
  725. r->internal.grouping_create= api_v1_data_groups[i].create;
  726. r->internal.grouping_reset = api_v1_data_groups[i].reset;
  727. r->internal.grouping_free = api_v1_data_groups[i].free;
  728. r->internal.grouping_add = api_v1_data_groups[i].add;
  729. r->internal.grouping_flush = api_v1_data_groups[i].flush;
  730. found = 1;
  731. }
  732. }
  733. if(!found) {
  734. errno = 0;
  735. #ifdef NETDATA_INTERNAL_CHECKS
  736. error("INTERNAL ERROR: grouping method %u not found for chart '%s'. Using 'average'", (unsigned int)group_method, r->st->name);
  737. #endif
  738. r->internal.grouping_create= grouping_create_average;
  739. r->internal.grouping_reset = grouping_reset_average;
  740. r->internal.grouping_free = grouping_free_average;
  741. r->internal.grouping_add = grouping_add_average;
  742. r->internal.grouping_flush = grouping_flush_average;
  743. }
  744. }
  745. // allocate any memory required by the grouping method
  746. r->internal.grouping_data = r->internal.grouping_create(r);
  747. // -------------------------------------------------------------------------
  748. // disable the not-wanted dimensions
  749. rrdset_check_rdlock(st);
  750. if(dimensions)
  751. rrdr_disable_not_selected_dimensions(r, options, dimensions);
  752. // -------------------------------------------------------------------------
  753. // do the work for each dimension
  754. time_t max_after = 0, min_before = 0;
  755. long max_rows = 0;
  756. RRDDIM *rd;
  757. long c, dimensions_used = 0, dimensions_nonzero = 0;
  758. for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
  759. // if we need a percentage, we need to calculate all dimensions
  760. if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) {
  761. if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED;
  762. continue;
  763. }
  764. r->od[c] |= RRDR_DIMENSION_SELECTED;
  765. // reset the grouping for the new dimension
  766. r->internal.grouping_reset(r);
  767. do_dimension(
  768. r
  769. , points_wanted
  770. , rd
  771. , c
  772. , after_wanted
  773. , before_wanted
  774. );
  775. if(r->od[c] & RRDR_DIMENSION_NONZERO)
  776. dimensions_nonzero++;
  777. // verify all dimensions are aligned
  778. if(unlikely(!dimensions_used)) {
  779. min_before = r->before;
  780. max_after = r->after;
  781. max_rows = r->rows;
  782. }
  783. else {
  784. if(r->after != max_after) {
  785. #ifdef NETDATA_INTERNAL_CHECKS
  786. error("INTERNAL ERROR: 'after' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  787. st->name, (size_t)max_after, rd->name, (size_t)r->after);
  788. #endif
  789. r->after = (r->after > max_after) ? r->after : max_after;
  790. }
  791. if(r->before != min_before) {
  792. #ifdef NETDATA_INTERNAL_CHECKS
  793. error("INTERNAL ERROR: 'before' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  794. st->name, (size_t)min_before, rd->name, (size_t)r->before);
  795. #endif
  796. r->before = (r->before < min_before) ? r->before : min_before;
  797. }
  798. if(r->rows != max_rows) {
  799. #ifdef NETDATA_INTERNAL_CHECKS
  800. error("INTERNAL ERROR: 'rows' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  801. st->name, (size_t)max_rows, rd->name, (size_t)r->rows);
  802. #endif
  803. r->rows = (r->rows > max_rows) ? r->rows : max_rows;
  804. }
  805. }
  806. dimensions_used++;
  807. }
  808. #ifdef NETDATA_INTERNAL_CHECKS
  809. if(r->internal.log)
  810. rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ r->internal.log);
  811. if(r->rows != points_wanted)
  812. rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "got 'points' is not wanted 'points'");
  813. if(aligned && (r->before % group) != 0)
  814. rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "'before' is not aligned but alignment is required");
  815. // 'after' should not be aligned, since we start inside the first group
  816. //if(aligned && (r->after % group) != 0)
  817. // rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "'after' is not aligned but alignment is required");
  818. if(r->before != before_requested)
  819. rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "chart is not aligned to requested 'before'");
  820. if(r->before != before_wanted)
  821. rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "got 'before' is not wanted 'before'");
  822. // reported 'after' varies, depending on group
  823. if(r->after != after_wanted)
  824. rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "got 'after' is not wanted 'after'");
  825. #endif
  826. // free all resources used by the grouping method
  827. r->internal.grouping_free(r);
  828. // when all the dimensions are zero, we should return all of them
  829. if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero)) {
  830. // all the dimensions are zero
  831. // mark them as NONZERO to send them all
  832. for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
  833. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  834. r->od[c] |= RRDR_DIMENSION_NONZERO;
  835. }
  836. }
  837. rrdr_query_completed(r->internal.db_points_read, r->internal.result_points_generated);
  838. return r;
  839. }