query.c 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624
  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, RRDDIM *temp_rd) {
  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 = temp_rd?temp_rd: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 = temp_rd?temp_rd: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_variablestep(
  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 = r->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. calculated_number min = r->min, max = r->max;
  353. size_t db_points_read = 0;
  354. time_t db_now = now;
  355. storage_number n_curr, n_prev = SN_EMPTY_SLOT;
  356. calculated_number value;
  357. for(rd->state->query_ops.init(rd, &handle, now, before_wanted) ; points_added < points_wanted ; now += dt) {
  358. // make sure we return data in the proper time range
  359. if (unlikely(now > before_wanted)) {
  360. #ifdef NETDATA_INTERNAL_CHECKS
  361. r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
  362. #endif
  363. break;
  364. }
  365. if (unlikely(now < after_wanted)) {
  366. #ifdef NETDATA_INTERNAL_CHECKS
  367. r->internal.log = "skipped, because attempted to access the db before 'wanted after'";
  368. #endif
  369. continue;
  370. }
  371. while (now >= db_now && (!rd->state->query_ops.is_finished(&handle) ||
  372. does_storage_number_exist(n_prev))) {
  373. value = NAN;
  374. if (does_storage_number_exist(n_prev)) {
  375. // use the previously read database value
  376. n_curr = n_prev;
  377. } else {
  378. // read the value from the database
  379. n_curr = rd->state->query_ops.next_metric(&handle, &db_now);
  380. }
  381. n_prev = SN_EMPTY_SLOT;
  382. // db_now has a different value than above
  383. if (likely(now >= db_now)) {
  384. if (likely(does_storage_number_exist(n_curr))) {
  385. value = unpack_storage_number(n_curr);
  386. if (likely(value != 0.0))
  387. values_in_group_non_zero++;
  388. if (unlikely(did_storage_number_reset(n_curr)))
  389. group_value_flags |= RRDR_VALUE_RESET;
  390. }
  391. } else {
  392. // We must postpone processing the value and fill the result with gaps instead
  393. if (likely(does_storage_number_exist(n_curr))) {
  394. n_prev = n_curr;
  395. }
  396. }
  397. // add this value to grouping
  398. r->internal.grouping_add(r, value);
  399. values_in_group++;
  400. db_points_read++;
  401. }
  402. if (0 == values_in_group) {
  403. // add NAN to grouping
  404. r->internal.grouping_add(r, NAN);
  405. }
  406. rrdr_line = rrdr_line_init(r, now, rrdr_line);
  407. if(unlikely(!min_date)) min_date = now;
  408. max_date = now;
  409. // find the place to store our values
  410. RRDR_VALUE_FLAGS *rrdr_value_options_ptr = &r->o[rrdr_line * r->d + dim_id_in_rrdr];
  411. // update the dimension options
  412. if(likely(values_in_group_non_zero))
  413. r->od[dim_id_in_rrdr] |= RRDR_DIMENSION_NONZERO;
  414. // store the specific point options
  415. *rrdr_value_options_ptr = group_value_flags;
  416. // store the value
  417. value = r->internal.grouping_flush(r, rrdr_value_options_ptr);
  418. r->v[rrdr_line * r->d + dim_id_in_rrdr] = value;
  419. if(likely(points_added || dim_id_in_rrdr)) {
  420. // find the min/max across all dimensions
  421. if(unlikely(value < min)) min = value;
  422. if(unlikely(value > max)) max = value;
  423. }
  424. else {
  425. // runs only when dim_id_in_rrdr == 0 && points_added == 0
  426. // so, on the first point added for the query.
  427. min = max = value;
  428. }
  429. points_added++;
  430. values_in_group = 0;
  431. group_value_flags = RRDR_VALUE_NOTHING;
  432. values_in_group_non_zero = 0;
  433. }
  434. rd->state->query_ops.finalize(&handle);
  435. r->internal.db_points_read += db_points_read;
  436. r->internal.result_points_generated += points_added;
  437. r->min = min;
  438. r->max = max;
  439. r->before = max_date;
  440. r->after = min_date - (r->group - 1) * dt;
  441. rrdr_done(r, rrdr_line);
  442. #ifdef NETDATA_INTERNAL_CHECKS
  443. if(unlikely(r->rows != points_added))
  444. 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);
  445. #endif
  446. }
  447. static inline void do_dimension_fixedstep(
  448. RRDR *r
  449. , long points_wanted
  450. , RRDDIM *rd
  451. , long dim_id_in_rrdr
  452. , time_t after_wanted
  453. , time_t before_wanted
  454. ){
  455. RRDSET *st = r->st;
  456. time_t
  457. now = after_wanted,
  458. dt = r->update_every / r->group, /* usually is st->update_every */
  459. max_date = 0,
  460. min_date = 0;
  461. long
  462. group_size = r->group,
  463. points_added = 0,
  464. values_in_group = 0,
  465. values_in_group_non_zero = 0,
  466. rrdr_line = -1;
  467. RRDR_VALUE_FLAGS
  468. group_value_flags = RRDR_VALUE_NOTHING;
  469. struct rrddim_query_handle handle;
  470. calculated_number min = r->min, max = r->max;
  471. size_t db_points_read = 0;
  472. time_t db_now = now;
  473. for(rd->state->query_ops.init(rd, &handle, now, before_wanted) ; points_added < points_wanted ; now += dt) {
  474. // make sure we return data in the proper time range
  475. if(unlikely(now > before_wanted)) {
  476. #ifdef NETDATA_INTERNAL_CHECKS
  477. r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
  478. #endif
  479. break;
  480. }
  481. if(unlikely(now < after_wanted)) {
  482. #ifdef NETDATA_INTERNAL_CHECKS
  483. r->internal.log = "skipped, because attempted to access the db before 'wanted after'";
  484. #endif
  485. continue;
  486. }
  487. // read the value from the database
  488. //storage_number n = rd->values[slot];
  489. #ifdef NETDATA_INTERNAL_CHECKS
  490. if ((rd->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) &&
  491. (rrdset_time2slot(st, now) != (long unsigned)handle.slotted.slot)) {
  492. error("INTERNAL CHECK: Unaligned query for %s, database slot: %lu, expected slot: %lu", rd->id, (long unsigned)handle.slotted.slot, rrdset_time2slot(st, now));
  493. }
  494. #endif
  495. db_now = now; // this is needed to set db_now in case the next_metric implementation does not set it
  496. storage_number n = rd->state->query_ops.next_metric(&handle, &db_now);
  497. if(unlikely(db_now > before_wanted)) {
  498. #ifdef NETDATA_INTERNAL_CHECKS
  499. r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
  500. #endif
  501. break;
  502. }
  503. for ( ; now <= db_now ; now += dt) {
  504. calculated_number value = NAN;
  505. if(likely(now >= db_now && does_storage_number_exist(n))) {
  506. #if defined(NETDATA_INTERNAL_CHECKS) && defined(ENABLE_DBENGINE)
  507. if ((rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) && (now != handle.rrdeng.now)) {
  508. error("INTERNAL CHECK: Unaligned query for %s, database time: %ld, expected time: %ld", rd->id, (long)handle.rrdeng.now, (long)now);
  509. }
  510. #endif
  511. value = unpack_storage_number(n);
  512. if(likely(value != 0.0))
  513. values_in_group_non_zero++;
  514. if(unlikely(did_storage_number_reset(n)))
  515. group_value_flags |= RRDR_VALUE_RESET;
  516. }
  517. // add this value for grouping
  518. r->internal.grouping_add(r, value);
  519. values_in_group++;
  520. db_points_read++;
  521. if(unlikely(values_in_group == group_size)) {
  522. rrdr_line = rrdr_line_init(r, now, rrdr_line);
  523. if(unlikely(!min_date)) min_date = now;
  524. max_date = now;
  525. // find the place to store our values
  526. RRDR_VALUE_FLAGS *rrdr_value_options_ptr = &r->o[rrdr_line * r->d + dim_id_in_rrdr];
  527. // update the dimension options
  528. if(likely(values_in_group_non_zero))
  529. r->od[dim_id_in_rrdr] |= RRDR_DIMENSION_NONZERO;
  530. // store the specific point options
  531. *rrdr_value_options_ptr = group_value_flags;
  532. // store the value
  533. calculated_number value = r->internal.grouping_flush(r, rrdr_value_options_ptr);
  534. r->v[rrdr_line * r->d + dim_id_in_rrdr] = value;
  535. if(likely(points_added || dim_id_in_rrdr)) {
  536. // find the min/max across all dimensions
  537. if(unlikely(value < min)) min = value;
  538. if(unlikely(value > max)) max = value;
  539. }
  540. else {
  541. // runs only when dim_id_in_rrdr == 0 && points_added == 0
  542. // so, on the first point added for the query.
  543. min = max = value;
  544. }
  545. points_added++;
  546. values_in_group = 0;
  547. group_value_flags = RRDR_VALUE_NOTHING;
  548. values_in_group_non_zero = 0;
  549. }
  550. }
  551. now = db_now;
  552. }
  553. rd->state->query_ops.finalize(&handle);
  554. r->internal.db_points_read += db_points_read;
  555. r->internal.result_points_generated += points_added;
  556. r->min = min;
  557. r->max = max;
  558. r->before = max_date;
  559. r->after = min_date - (r->group - 1) * dt;
  560. rrdr_done(r, rrdr_line);
  561. #ifdef NETDATA_INTERNAL_CHECKS
  562. if(unlikely(r->rows != points_added))
  563. 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);
  564. #endif
  565. }
  566. // ----------------------------------------------------------------------------
  567. // fill RRDR for the whole chart
  568. #ifdef NETDATA_INTERNAL_CHECKS
  569. static void rrd2rrdr_log_request_response_metdata(RRDR *r
  570. , RRDR_GROUPING group_method
  571. , int aligned
  572. , long group
  573. , long resampling_time
  574. , long resampling_group
  575. , time_t after_wanted
  576. , time_t after_requested
  577. , time_t before_wanted
  578. , time_t before_requested
  579. , long points_requested
  580. , long points_wanted
  581. //, size_t after_slot
  582. //, size_t before_slot
  583. , const char *msg
  584. ) {
  585. info("INTERNAL ERROR: rrd2rrdr() on %s update every %d with %s grouping %s (group: %ld, resampling_time: %ld, resampling_group: %ld), "
  586. "after (got: %zu, want: %zu, req: %zu, db: %zu), "
  587. "before (got: %zu, want: %zu, req: %zu, db: %zu), "
  588. "duration (got: %zu, want: %zu, req: %zu, db: %zu), "
  589. //"slot (after: %zu, before: %zu, delta: %zu), "
  590. "points (got: %ld, want: %ld, req: %ld, db: %ld), "
  591. "%s"
  592. , r->st->name
  593. , r->st->update_every
  594. // grouping
  595. , (aligned) ? "aligned" : "unaligned"
  596. , group_method2string(group_method)
  597. , group
  598. , resampling_time
  599. , resampling_group
  600. // after
  601. , (size_t)r->after
  602. , (size_t)after_wanted
  603. , (size_t)after_requested
  604. , (size_t)rrdset_first_entry_t(r->st)
  605. // before
  606. , (size_t)r->before
  607. , (size_t)before_wanted
  608. , (size_t)before_requested
  609. , (size_t)rrdset_last_entry_t(r->st)
  610. // duration
  611. , (size_t)(r->before - r->after + r->st->update_every)
  612. , (size_t)(before_wanted - after_wanted + r->st->update_every)
  613. , (size_t)(before_requested - after_requested)
  614. , (size_t)((rrdset_last_entry_t(r->st) - rrdset_first_entry_t(r->st)) + r->st->update_every)
  615. // slot
  616. /*
  617. , after_slot
  618. , before_slot
  619. , (after_slot > before_slot) ? (r->st->entries - after_slot + before_slot) : (before_slot - after_slot)
  620. */
  621. // points
  622. , r->rows
  623. , points_wanted
  624. , points_requested
  625. , r->st->entries
  626. // message
  627. , msg
  628. );
  629. }
  630. #endif // NETDATA_INTERNAL_CHECKS
  631. // Returns 1 if an absolute period was requested or 0 if it was a relative period
  632. static int rrdr_convert_before_after_to_absolute(
  633. long long *after_requestedp
  634. , long long *before_requestedp
  635. , int update_every
  636. , time_t first_entry_t
  637. , time_t last_entry_t
  638. ) {
  639. int absolute_period_requested = -1;
  640. long long after_requested, before_requested;
  641. before_requested = *before_requestedp;
  642. after_requested = *after_requestedp;
  643. if(before_requested == 0 && after_requested == 0) {
  644. // dump the all the data
  645. before_requested = last_entry_t;
  646. after_requested = first_entry_t;
  647. absolute_period_requested = 0;
  648. }
  649. // allow relative for before (smaller than API_RELATIVE_TIME_MAX)
  650. if(abs(before_requested) <= API_RELATIVE_TIME_MAX) {
  651. if(abs(before_requested) % update_every) {
  652. // make sure it is multiple of st->update_every
  653. if(before_requested < 0) before_requested = before_requested - update_every -
  654. before_requested % update_every;
  655. else before_requested = before_requested + update_every - before_requested % update_every;
  656. }
  657. if(before_requested > 0) before_requested = first_entry_t + before_requested;
  658. else before_requested = last_entry_t + before_requested; //last_entry_t is not really now_t
  659. //TODO: fix before_requested to be relative to now_t
  660. absolute_period_requested = 0;
  661. }
  662. // allow relative for after (smaller than API_RELATIVE_TIME_MAX)
  663. if(abs(after_requested) <= API_RELATIVE_TIME_MAX) {
  664. if(after_requested == 0) after_requested = -update_every;
  665. if(abs(after_requested) % update_every) {
  666. // make sure it is multiple of st->update_every
  667. if(after_requested < 0) after_requested = after_requested - update_every - after_requested % update_every;
  668. else after_requested = after_requested + update_every - after_requested % update_every;
  669. }
  670. after_requested = before_requested + after_requested;
  671. absolute_period_requested = 0;
  672. }
  673. if(absolute_period_requested == -1)
  674. absolute_period_requested = 1;
  675. // make sure they are within our timeframe
  676. if(before_requested > last_entry_t) before_requested = last_entry_t;
  677. if(before_requested < first_entry_t) before_requested = first_entry_t;
  678. if(after_requested > last_entry_t) after_requested = last_entry_t;
  679. if(after_requested < first_entry_t) after_requested = first_entry_t;
  680. // check if they are reversed
  681. if(after_requested > before_requested) {
  682. time_t tmp = before_requested;
  683. before_requested = after_requested;
  684. after_requested = tmp;
  685. }
  686. *before_requestedp = before_requested;
  687. *after_requestedp = after_requested;
  688. return absolute_period_requested;
  689. }
  690. static RRDR *rrd2rrdr_fixedstep(
  691. RRDSET *st
  692. , long points_requested
  693. , long long after_requested
  694. , long long before_requested
  695. , RRDR_GROUPING group_method
  696. , long resampling_time_requested
  697. , RRDR_OPTIONS options
  698. , const char *dimensions
  699. , int update_every
  700. , time_t first_entry_t
  701. , time_t last_entry_t
  702. , int absolute_period_requested
  703. , struct context_param *context_param_list
  704. ) {
  705. int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
  706. // the duration of the chart
  707. time_t duration = before_requested - after_requested;
  708. long available_points = duration / update_every;
  709. RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL;
  710. if(duration <= 0 || available_points <= 0)
  711. return rrdr_create(st, 1, context_param_list);
  712. // check the number of wanted points in the result
  713. if(unlikely(points_requested < 0)) points_requested = -points_requested;
  714. if(unlikely(points_requested > available_points)) points_requested = available_points;
  715. if(unlikely(points_requested == 0)) points_requested = available_points;
  716. // calculate the desired grouping of source data points
  717. long group = available_points / points_requested;
  718. if(unlikely(group <= 0)) group = 1;
  719. if(unlikely(available_points % points_requested > points_requested / 2)) group++; // rounding to the closest integer
  720. // resampling_time_requested enforces a certain grouping multiple
  721. calculated_number resampling_divisor = 1.0;
  722. long resampling_group = 1;
  723. if(unlikely(resampling_time_requested > update_every)) {
  724. if (unlikely(resampling_time_requested > duration)) {
  725. // group_time is above the available duration
  726. #ifdef NETDATA_INTERNAL_CHECKS
  727. info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, resampling_time_requested, duration);
  728. #endif
  729. after_requested = before_requested - resampling_time_requested;
  730. duration = before_requested - after_requested;
  731. available_points = duration / update_every;
  732. group = available_points / points_requested;
  733. }
  734. // if the duration is not aligned to resampling time
  735. // extend the duration to the past, to avoid a gap at the chart
  736. // only when the missing duration is above 1/10th of a point
  737. if(duration % resampling_time_requested) {
  738. time_t delta = duration % resampling_time_requested;
  739. if(delta > resampling_time_requested / 10) {
  740. after_requested -= resampling_time_requested - delta;
  741. duration = before_requested - after_requested;
  742. available_points = duration / update_every;
  743. group = available_points / points_requested;
  744. }
  745. }
  746. // the points we should group to satisfy gtime
  747. resampling_group = resampling_time_requested / update_every;
  748. if(unlikely(resampling_time_requested % update_every)) {
  749. #ifdef NETDATA_INTERNAL_CHECKS
  750. 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, update_every);
  751. #endif
  752. resampling_group++;
  753. }
  754. // adapt group according to resampling_group
  755. if(unlikely(group < resampling_group)) group = resampling_group; // do not allow grouping below the desired one
  756. if(unlikely(group % resampling_group)) group += resampling_group - (group % resampling_group); // make sure group is multiple of resampling_group
  757. //resampling_divisor = group / resampling_group;
  758. resampling_divisor = (calculated_number)(group * update_every) / (calculated_number)resampling_time_requested;
  759. }
  760. // now that we have group,
  761. // align the requested timeframe to fit it.
  762. if(aligned) {
  763. // alignement has been requested, so align the values
  764. before_requested -= before_requested % (group * update_every);
  765. after_requested -= after_requested % (group * update_every);
  766. }
  767. // we align the request on requested_before
  768. time_t before_wanted = before_requested;
  769. if(likely(before_wanted > last_entry_t)) {
  770. #ifdef NETDATA_INTERNAL_CHECKS
  771. error("INTERNAL ERROR: rrd2rrdr() on %s, before_wanted is after db max", st->name);
  772. #endif
  773. before_wanted = last_entry_t - (last_entry_t % ( ((aligned)?group:1) * update_every ));
  774. }
  775. //size_t before_slot = rrdset_time2slot(st, before_wanted);
  776. // we need to estimate the number of points, for having
  777. // an integer number of values per point
  778. long points_wanted = (before_wanted - after_requested) / (update_every * group);
  779. time_t after_wanted = before_wanted - (points_wanted * group * update_every) + update_every;
  780. if(unlikely(after_wanted < first_entry_t)) {
  781. // hm... we go to the past, calculate again points_wanted using all the db from before_wanted to the beginning
  782. points_wanted = (before_wanted - first_entry_t) / group;
  783. // recalculate after wanted with the new number of points
  784. after_wanted = before_wanted - (points_wanted * group * update_every) + update_every;
  785. if(unlikely(after_wanted < first_entry_t)) {
  786. #ifdef NETDATA_INTERNAL_CHECKS
  787. error("INTERNAL ERROR: rrd2rrdr() on %s, after_wanted is before db min", st->name);
  788. #endif
  789. after_wanted = first_entry_t - (first_entry_t % ( ((aligned)?group:1) * update_every )) + ( ((aligned)?group:1) * update_every );
  790. }
  791. }
  792. //size_t after_slot = rrdset_time2slot(st, after_wanted);
  793. // check if they are reversed
  794. if(unlikely(after_wanted > before_wanted)) {
  795. #ifdef NETDATA_INTERNAL_CHECKS
  796. error("INTERNAL ERROR: rrd2rrdr() on %s, reversed wanted after/before", st->name);
  797. #endif
  798. time_t tmp = before_wanted;
  799. before_wanted = after_wanted;
  800. after_wanted = tmp;
  801. }
  802. // recalculate points_wanted using the final time-frame
  803. points_wanted = (before_wanted - after_wanted) / update_every / group + 1;
  804. if(unlikely(points_wanted < 0)) {
  805. #ifdef NETDATA_INTERNAL_CHECKS
  806. error("INTERNAL ERROR: rrd2rrdr() on %s, points_wanted is %ld", st->name, points_wanted);
  807. #endif
  808. points_wanted = 0;
  809. }
  810. #ifdef NETDATA_INTERNAL_CHECKS
  811. duration = before_wanted - after_wanted;
  812. if(after_wanted < first_entry_t)
  813. error("INTERNAL CHECK: after_wanted %u is too small, minimum %u", (uint32_t)after_wanted, (uint32_t)first_entry_t);
  814. if(after_wanted > last_entry_t)
  815. error("INTERNAL CHECK: after_wanted %u is too big, maximum %u", (uint32_t)after_wanted, (uint32_t)last_entry_t);
  816. if(before_wanted < first_entry_t)
  817. error("INTERNAL CHECK: before_wanted %u is too small, minimum %u", (uint32_t)before_wanted, (uint32_t)first_entry_t);
  818. if(before_wanted > last_entry_t)
  819. error("INTERNAL CHECK: before_wanted %u is too big, maximum %u", (uint32_t)before_wanted, (uint32_t)last_entry_t);
  820. /*
  821. if(before_slot >= (size_t)st->entries)
  822. error("INTERNAL CHECK: before_slot is invalid %zu, expected 0 to %ld", before_slot, st->entries - 1);
  823. if(after_slot >= (size_t)st->entries)
  824. error("INTERNAL CHECK: after_slot is invalid %zu, expected 0 to %ld", after_slot, st->entries - 1);
  825. */
  826. if(points_wanted > (before_wanted - after_wanted) / group / update_every + 1)
  827. error("INTERNAL CHECK: points_wanted %ld is more than points %ld", points_wanted, (before_wanted - after_wanted) / group / update_every + 1);
  828. if(group < resampling_group)
  829. error("INTERNAL CHECK: group %ld is less than the desired group points %ld", group, resampling_group);
  830. if(group > resampling_group && group % resampling_group)
  831. error("INTERNAL CHECK: group %ld is not a multiple of the desired group points %ld", group, resampling_group);
  832. #endif
  833. // -------------------------------------------------------------------------
  834. // initialize our result set
  835. // this also locks the chart for us
  836. RRDR *r = rrdr_create(st, points_wanted, context_param_list);
  837. if(unlikely(!r)) {
  838. #ifdef NETDATA_INTERNAL_CHECKS
  839. 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);
  840. #endif
  841. return NULL;
  842. }
  843. if(unlikely(!r->d || !points_wanted)) {
  844. #ifdef NETDATA_INTERNAL_CHECKS
  845. 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);
  846. #endif
  847. return r;
  848. }
  849. if(unlikely(absolute_period_requested == 1))
  850. r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
  851. else
  852. r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
  853. // find how many dimensions we have
  854. long dimensions_count = r->d;
  855. // -------------------------------------------------------------------------
  856. // initialize RRDR
  857. r->group = group;
  858. r->update_every = (int)group * update_every;
  859. r->before = before_wanted;
  860. r->after = after_wanted;
  861. r->internal.points_wanted = points_wanted;
  862. r->internal.resampling_group = resampling_group;
  863. r->internal.resampling_divisor = resampling_divisor;
  864. // -------------------------------------------------------------------------
  865. // assign the processor functions
  866. {
  867. int i, found = 0;
  868. for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
  869. if(api_v1_data_groups[i].value == group_method) {
  870. r->internal.grouping_create= api_v1_data_groups[i].create;
  871. r->internal.grouping_reset = api_v1_data_groups[i].reset;
  872. r->internal.grouping_free = api_v1_data_groups[i].free;
  873. r->internal.grouping_add = api_v1_data_groups[i].add;
  874. r->internal.grouping_flush = api_v1_data_groups[i].flush;
  875. found = 1;
  876. }
  877. }
  878. if(!found) {
  879. errno = 0;
  880. #ifdef NETDATA_INTERNAL_CHECKS
  881. error("INTERNAL ERROR: grouping method %u not found for chart '%s'. Using 'average'", (unsigned int)group_method, r->st->name);
  882. #endif
  883. r->internal.grouping_create= grouping_create_average;
  884. r->internal.grouping_reset = grouping_reset_average;
  885. r->internal.grouping_free = grouping_free_average;
  886. r->internal.grouping_add = grouping_add_average;
  887. r->internal.grouping_flush = grouping_flush_average;
  888. }
  889. }
  890. // allocate any memory required by the grouping method
  891. r->internal.grouping_data = r->internal.grouping_create(r);
  892. // -------------------------------------------------------------------------
  893. // disable the not-wanted dimensions
  894. rrdset_check_rdlock(st);
  895. if(dimensions)
  896. rrdr_disable_not_selected_dimensions(r, options, dimensions, temp_rd);
  897. // -------------------------------------------------------------------------
  898. // do the work for each dimension
  899. time_t max_after = 0, min_before = 0;
  900. long max_rows = 0;
  901. RRDDIM *rd;
  902. long c, dimensions_used = 0, dimensions_nonzero = 0;
  903. for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
  904. // if we need a percentage, we need to calculate all dimensions
  905. if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) {
  906. if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED;
  907. continue;
  908. }
  909. r->od[c] |= RRDR_DIMENSION_SELECTED;
  910. // reset the grouping for the new dimension
  911. r->internal.grouping_reset(r);
  912. do_dimension_fixedstep(
  913. r
  914. , points_wanted
  915. , rd
  916. , c
  917. , after_wanted
  918. , before_wanted
  919. );
  920. if(r->od[c] & RRDR_DIMENSION_NONZERO)
  921. dimensions_nonzero++;
  922. // verify all dimensions are aligned
  923. if(unlikely(!dimensions_used)) {
  924. min_before = r->before;
  925. max_after = r->after;
  926. max_rows = r->rows;
  927. }
  928. else {
  929. if(r->after != max_after) {
  930. #ifdef NETDATA_INTERNAL_CHECKS
  931. error("INTERNAL ERROR: 'after' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  932. st->name, (size_t)max_after, rd->name, (size_t)r->after);
  933. #endif
  934. r->after = (r->after > max_after) ? r->after : max_after;
  935. }
  936. if(r->before != min_before) {
  937. #ifdef NETDATA_INTERNAL_CHECKS
  938. error("INTERNAL ERROR: 'before' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  939. st->name, (size_t)min_before, rd->name, (size_t)r->before);
  940. #endif
  941. r->before = (r->before < min_before) ? r->before : min_before;
  942. }
  943. if(r->rows != max_rows) {
  944. #ifdef NETDATA_INTERNAL_CHECKS
  945. error("INTERNAL ERROR: 'rows' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  946. st->name, (size_t)max_rows, rd->name, (size_t)r->rows);
  947. #endif
  948. r->rows = (r->rows > max_rows) ? r->rows : max_rows;
  949. }
  950. }
  951. dimensions_used++;
  952. }
  953. #ifdef NETDATA_INTERNAL_CHECKS
  954. if (dimensions_used) {
  955. if(r->internal.log)
  956. 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);
  957. if(r->rows != points_wanted)
  958. 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'");
  959. if(aligned && (r->before % group) != 0)
  960. 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");
  961. // 'after' should not be aligned, since we start inside the first group
  962. //if(aligned && (r->after % group) != 0)
  963. // 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");
  964. if(r->before != before_requested)
  965. 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'");
  966. if(r->before != before_wanted)
  967. 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'");
  968. // reported 'after' varies, depending on group
  969. if(r->after != after_wanted)
  970. 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'");
  971. }
  972. #endif
  973. // free all resources used by the grouping method
  974. r->internal.grouping_free(r);
  975. // when all the dimensions are zero, we should return all of them
  976. if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero)) {
  977. // all the dimensions are zero
  978. // mark them as NONZERO to send them all
  979. for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
  980. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  981. r->od[c] |= RRDR_DIMENSION_NONZERO;
  982. }
  983. }
  984. rrdr_query_completed(r->internal.db_points_read, r->internal.result_points_generated);
  985. return r;
  986. }
  987. #ifdef ENABLE_DBENGINE
  988. static RRDR *rrd2rrdr_variablestep(
  989. RRDSET *st
  990. , long points_requested
  991. , long long after_requested
  992. , long long before_requested
  993. , RRDR_GROUPING group_method
  994. , long resampling_time_requested
  995. , RRDR_OPTIONS options
  996. , const char *dimensions
  997. , int update_every
  998. , time_t first_entry_t
  999. , time_t last_entry_t
  1000. , int absolute_period_requested
  1001. , struct rrdeng_region_info *region_info_array
  1002. , struct context_param *context_param_list
  1003. ) {
  1004. int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
  1005. // the duration of the chart
  1006. time_t duration = before_requested - after_requested;
  1007. long available_points = duration / update_every;
  1008. RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL;
  1009. if(duration <= 0 || available_points <= 0) {
  1010. freez(region_info_array);
  1011. return rrdr_create(st, 1, context_param_list);
  1012. }
  1013. // check the number of wanted points in the result
  1014. if(unlikely(points_requested < 0)) points_requested = -points_requested;
  1015. if(unlikely(points_requested > available_points)) points_requested = available_points;
  1016. if(unlikely(points_requested == 0)) points_requested = available_points;
  1017. // calculate the desired grouping of source data points
  1018. long group = available_points / points_requested;
  1019. if(unlikely(group <= 0)) group = 1;
  1020. if(unlikely(available_points % points_requested > points_requested / 2)) group++; // rounding to the closest integer
  1021. // resampling_time_requested enforces a certain grouping multiple
  1022. calculated_number resampling_divisor = 1.0;
  1023. long resampling_group = 1;
  1024. if(unlikely(resampling_time_requested > update_every)) {
  1025. if (unlikely(resampling_time_requested > duration)) {
  1026. // group_time is above the available duration
  1027. #ifdef NETDATA_INTERNAL_CHECKS
  1028. info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, resampling_time_requested, duration);
  1029. #endif
  1030. after_requested = before_requested - resampling_time_requested;
  1031. duration = before_requested - after_requested;
  1032. available_points = duration / update_every;
  1033. group = available_points / points_requested;
  1034. }
  1035. // if the duration is not aligned to resampling time
  1036. // extend the duration to the past, to avoid a gap at the chart
  1037. // only when the missing duration is above 1/10th of a point
  1038. if(duration % resampling_time_requested) {
  1039. time_t delta = duration % resampling_time_requested;
  1040. if(delta > resampling_time_requested / 10) {
  1041. after_requested -= resampling_time_requested - delta;
  1042. duration = before_requested - after_requested;
  1043. available_points = duration / update_every;
  1044. group = available_points / points_requested;
  1045. }
  1046. }
  1047. // the points we should group to satisfy gtime
  1048. resampling_group = resampling_time_requested / update_every;
  1049. if(unlikely(resampling_time_requested % update_every)) {
  1050. #ifdef NETDATA_INTERNAL_CHECKS
  1051. 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, update_every);
  1052. #endif
  1053. resampling_group++;
  1054. }
  1055. // adapt group according to resampling_group
  1056. if(unlikely(group < resampling_group)) group = resampling_group; // do not allow grouping below the desired one
  1057. if(unlikely(group % resampling_group)) group += resampling_group - (group % resampling_group); // make sure group is multiple of resampling_group
  1058. //resampling_divisor = group / resampling_group;
  1059. resampling_divisor = (calculated_number)(group * update_every) / (calculated_number)resampling_time_requested;
  1060. }
  1061. // now that we have group,
  1062. // align the requested timeframe to fit it.
  1063. if(aligned) {
  1064. // alignement has been requested, so align the values
  1065. before_requested -= before_requested % (group * update_every);
  1066. after_requested -= after_requested % (group * update_every);
  1067. }
  1068. // we align the request on requested_before
  1069. time_t before_wanted = before_requested;
  1070. if(likely(before_wanted > last_entry_t)) {
  1071. #ifdef NETDATA_INTERNAL_CHECKS
  1072. error("INTERNAL ERROR: rrd2rrdr() on %s, before_wanted is after db max", st->name);
  1073. #endif
  1074. before_wanted = last_entry_t - (last_entry_t % ( ((aligned)?group:1) * update_every ));
  1075. }
  1076. //size_t before_slot = rrdset_time2slot(st, before_wanted);
  1077. // we need to estimate the number of points, for having
  1078. // an integer number of values per point
  1079. long points_wanted = (before_wanted - after_requested) / (update_every * group);
  1080. time_t after_wanted = before_wanted - (points_wanted * group * update_every) + update_every;
  1081. if(unlikely(after_wanted < first_entry_t)) {
  1082. // hm... we go to the past, calculate again points_wanted using all the db from before_wanted to the beginning
  1083. points_wanted = (before_wanted - first_entry_t) / group;
  1084. // recalculate after wanted with the new number of points
  1085. after_wanted = before_wanted - (points_wanted * group * update_every) + update_every;
  1086. if(unlikely(after_wanted < first_entry_t)) {
  1087. #ifdef NETDATA_INTERNAL_CHECKS
  1088. error("INTERNAL ERROR: rrd2rrdr() on %s, after_wanted is before db min", st->name);
  1089. #endif
  1090. after_wanted = first_entry_t - (first_entry_t % ( ((aligned)?group:1) * update_every )) + ( ((aligned)?group:1) * update_every );
  1091. }
  1092. }
  1093. //size_t after_slot = rrdset_time2slot(st, after_wanted);
  1094. // check if they are reversed
  1095. if(unlikely(after_wanted > before_wanted)) {
  1096. #ifdef NETDATA_INTERNAL_CHECKS
  1097. error("INTERNAL ERROR: rrd2rrdr() on %s, reversed wanted after/before", st->name);
  1098. #endif
  1099. time_t tmp = before_wanted;
  1100. before_wanted = after_wanted;
  1101. after_wanted = tmp;
  1102. }
  1103. // recalculate points_wanted using the final time-frame
  1104. points_wanted = (before_wanted - after_wanted) / update_every / group + 1;
  1105. if(unlikely(points_wanted < 0)) {
  1106. #ifdef NETDATA_INTERNAL_CHECKS
  1107. error("INTERNAL ERROR: rrd2rrdr() on %s, points_wanted is %ld", st->name, points_wanted);
  1108. #endif
  1109. points_wanted = 0;
  1110. }
  1111. #ifdef NETDATA_INTERNAL_CHECKS
  1112. duration = before_wanted - after_wanted;
  1113. if(after_wanted < first_entry_t)
  1114. error("INTERNAL CHECK: after_wanted %u is too small, minimum %u", (uint32_t)after_wanted, (uint32_t)first_entry_t);
  1115. if(after_wanted > last_entry_t)
  1116. error("INTERNAL CHECK: after_wanted %u is too big, maximum %u", (uint32_t)after_wanted, (uint32_t)last_entry_t);
  1117. if(before_wanted < first_entry_t)
  1118. error("INTERNAL CHECK: before_wanted %u is too small, minimum %u", (uint32_t)before_wanted, (uint32_t)first_entry_t);
  1119. if(before_wanted > last_entry_t)
  1120. error("INTERNAL CHECK: before_wanted %u is too big, maximum %u", (uint32_t)before_wanted, (uint32_t)last_entry_t);
  1121. /*
  1122. if(before_slot >= (size_t)st->entries)
  1123. error("INTERNAL CHECK: before_slot is invalid %zu, expected 0 to %ld", before_slot, st->entries - 1);
  1124. if(after_slot >= (size_t)st->entries)
  1125. error("INTERNAL CHECK: after_slot is invalid %zu, expected 0 to %ld", after_slot, st->entries - 1);
  1126. */
  1127. if(points_wanted > (before_wanted - after_wanted) / group / update_every + 1)
  1128. error("INTERNAL CHECK: points_wanted %ld is more than points %ld", points_wanted, (before_wanted - after_wanted) / group / update_every + 1);
  1129. if(group < resampling_group)
  1130. error("INTERNAL CHECK: group %ld is less than the desired group points %ld", group, resampling_group);
  1131. if(group > resampling_group && group % resampling_group)
  1132. error("INTERNAL CHECK: group %ld is not a multiple of the desired group points %ld", group, resampling_group);
  1133. #endif
  1134. // -------------------------------------------------------------------------
  1135. // initialize our result set
  1136. // this also locks the chart for us
  1137. RRDR *r = rrdr_create(st, points_wanted, context_param_list);
  1138. if(unlikely(!r)) {
  1139. #ifdef NETDATA_INTERNAL_CHECKS
  1140. 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);
  1141. #endif
  1142. freez(region_info_array);
  1143. return NULL;
  1144. }
  1145. if(unlikely(!r->d || !points_wanted)) {
  1146. #ifdef NETDATA_INTERNAL_CHECKS
  1147. 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);
  1148. #endif
  1149. freez(region_info_array);
  1150. return r;
  1151. }
  1152. r->result_options |= RRDR_RESULT_OPTION_VARIABLE_STEP;
  1153. if(unlikely(absolute_period_requested == 1))
  1154. r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
  1155. else
  1156. r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
  1157. // find how many dimensions we have
  1158. long dimensions_count = r->d;
  1159. // -------------------------------------------------------------------------
  1160. // initialize RRDR
  1161. r->group = group;
  1162. r->update_every = (int)group * update_every;
  1163. r->before = before_wanted;
  1164. r->after = after_wanted;
  1165. r->internal.points_wanted = points_wanted;
  1166. r->internal.resampling_group = resampling_group;
  1167. r->internal.resampling_divisor = resampling_divisor;
  1168. // -------------------------------------------------------------------------
  1169. // assign the processor functions
  1170. {
  1171. int i, found = 0;
  1172. for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
  1173. if(api_v1_data_groups[i].value == group_method) {
  1174. r->internal.grouping_create= api_v1_data_groups[i].create;
  1175. r->internal.grouping_reset = api_v1_data_groups[i].reset;
  1176. r->internal.grouping_free = api_v1_data_groups[i].free;
  1177. r->internal.grouping_add = api_v1_data_groups[i].add;
  1178. r->internal.grouping_flush = api_v1_data_groups[i].flush;
  1179. found = 1;
  1180. }
  1181. }
  1182. if(!found) {
  1183. errno = 0;
  1184. #ifdef NETDATA_INTERNAL_CHECKS
  1185. error("INTERNAL ERROR: grouping method %u not found for chart '%s'. Using 'average'", (unsigned int)group_method, r->st->name);
  1186. #endif
  1187. r->internal.grouping_create= grouping_create_average;
  1188. r->internal.grouping_reset = grouping_reset_average;
  1189. r->internal.grouping_free = grouping_free_average;
  1190. r->internal.grouping_add = grouping_add_average;
  1191. r->internal.grouping_flush = grouping_flush_average;
  1192. }
  1193. }
  1194. // allocate any memory required by the grouping method
  1195. r->internal.grouping_data = r->internal.grouping_create(r);
  1196. // -------------------------------------------------------------------------
  1197. // disable the not-wanted dimensions
  1198. rrdset_check_rdlock(st);
  1199. if(dimensions)
  1200. rrdr_disable_not_selected_dimensions(r, options, dimensions, temp_rd);
  1201. // -------------------------------------------------------------------------
  1202. // do the work for each dimension
  1203. time_t max_after = 0, min_before = 0;
  1204. long max_rows = 0;
  1205. RRDDIM *rd;
  1206. long c, dimensions_used = 0, dimensions_nonzero = 0;
  1207. for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
  1208. // if we need a percentage, we need to calculate all dimensions
  1209. if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) {
  1210. if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED;
  1211. continue;
  1212. }
  1213. r->od[c] |= RRDR_DIMENSION_SELECTED;
  1214. // reset the grouping for the new dimension
  1215. r->internal.grouping_reset(r);
  1216. do_dimension_variablestep(
  1217. r
  1218. , points_wanted
  1219. , rd
  1220. , c
  1221. , after_wanted
  1222. , before_wanted
  1223. );
  1224. if(r->od[c] & RRDR_DIMENSION_NONZERO)
  1225. dimensions_nonzero++;
  1226. // verify all dimensions are aligned
  1227. if(unlikely(!dimensions_used)) {
  1228. min_before = r->before;
  1229. max_after = r->after;
  1230. max_rows = r->rows;
  1231. }
  1232. else {
  1233. if(r->after != max_after) {
  1234. #ifdef NETDATA_INTERNAL_CHECKS
  1235. error("INTERNAL ERROR: 'after' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  1236. st->name, (size_t)max_after, rd->name, (size_t)r->after);
  1237. #endif
  1238. r->after = (r->after > max_after) ? r->after : max_after;
  1239. }
  1240. if(r->before != min_before) {
  1241. #ifdef NETDATA_INTERNAL_CHECKS
  1242. error("INTERNAL ERROR: 'before' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  1243. st->name, (size_t)min_before, rd->name, (size_t)r->before);
  1244. #endif
  1245. r->before = (r->before < min_before) ? r->before : min_before;
  1246. }
  1247. if(r->rows != max_rows) {
  1248. #ifdef NETDATA_INTERNAL_CHECKS
  1249. error("INTERNAL ERROR: 'rows' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
  1250. st->name, (size_t)max_rows, rd->name, (size_t)r->rows);
  1251. #endif
  1252. r->rows = (r->rows > max_rows) ? r->rows : max_rows;
  1253. }
  1254. }
  1255. dimensions_used++;
  1256. }
  1257. #ifdef NETDATA_INTERNAL_CHECKS
  1258. if (dimensions_used) {
  1259. if(r->internal.log)
  1260. 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);
  1261. if(r->rows != points_wanted)
  1262. 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'");
  1263. if(aligned && (r->before % group) != 0)
  1264. 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");
  1265. // 'after' should not be aligned, since we start inside the first group
  1266. //if(aligned && (r->after % group) != 0)
  1267. // 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");
  1268. if(r->before != before_requested)
  1269. 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'");
  1270. if(r->before != before_wanted)
  1271. 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'");
  1272. // reported 'after' varies, depending on group
  1273. if(r->after != after_wanted)
  1274. 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'");
  1275. }
  1276. #endif
  1277. // free all resources used by the grouping method
  1278. r->internal.grouping_free(r);
  1279. // when all the dimensions are zero, we should return all of them
  1280. if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero)) {
  1281. // all the dimensions are zero
  1282. // mark them as NONZERO to send them all
  1283. for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
  1284. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  1285. r->od[c] |= RRDR_DIMENSION_NONZERO;
  1286. }
  1287. }
  1288. rrdr_query_completed(r->internal.db_points_read, r->internal.result_points_generated);
  1289. freez(region_info_array);
  1290. return r;
  1291. }
  1292. #endif //#ifdef ENABLE_DBENGINE
  1293. RRDR *rrd2rrdr(
  1294. RRDSET *st
  1295. , long points_requested
  1296. , long long after_requested
  1297. , long long before_requested
  1298. , RRDR_GROUPING group_method
  1299. , long resampling_time_requested
  1300. , RRDR_OPTIONS options
  1301. , const char *dimensions
  1302. , struct context_param *context_param_list
  1303. )
  1304. {
  1305. int rrd_update_every;
  1306. int absolute_period_requested;
  1307. // RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL;
  1308. time_t first_entry_t;
  1309. time_t last_entry_t;
  1310. if (context_param_list) {
  1311. first_entry_t = context_param_list->first_entry_t;
  1312. last_entry_t = context_param_list->last_entry_t;
  1313. } else {
  1314. first_entry_t = rrdset_first_entry_t(st);
  1315. last_entry_t = rrdset_last_entry_t(st);
  1316. }
  1317. rrd_update_every = st->update_every;
  1318. absolute_period_requested = rrdr_convert_before_after_to_absolute(&after_requested, &before_requested,
  1319. rrd_update_every, first_entry_t,
  1320. last_entry_t);
  1321. #ifdef ENABLE_DBENGINE
  1322. if (st->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
  1323. struct rrdeng_region_info *region_info_array;
  1324. unsigned regions, max_interval;
  1325. /* This call takes the chart read-lock */
  1326. regions = rrdeng_variable_step_boundaries(st, after_requested, before_requested,
  1327. &region_info_array, &max_interval, context_param_list);
  1328. if (1 == regions) {
  1329. if (region_info_array) {
  1330. if (rrd_update_every != region_info_array[0].update_every) {
  1331. rrd_update_every = region_info_array[0].update_every;
  1332. /* recalculate query alignment */
  1333. absolute_period_requested =
  1334. rrdr_convert_before_after_to_absolute(&after_requested, &before_requested, rrd_update_every,
  1335. first_entry_t, last_entry_t);
  1336. }
  1337. freez(region_info_array);
  1338. }
  1339. return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
  1340. resampling_time_requested, options, dimensions, rrd_update_every,
  1341. first_entry_t, last_entry_t, absolute_period_requested, context_param_list);
  1342. } else {
  1343. if (rrd_update_every != (uint16_t)max_interval) {
  1344. rrd_update_every = (uint16_t) max_interval;
  1345. /* recalculate query alignment */
  1346. absolute_period_requested = rrdr_convert_before_after_to_absolute(&after_requested, &before_requested,
  1347. rrd_update_every, first_entry_t,
  1348. last_entry_t);
  1349. }
  1350. return rrd2rrdr_variablestep(st, points_requested, after_requested, before_requested, group_method,
  1351. resampling_time_requested, options, dimensions, rrd_update_every,
  1352. first_entry_t, last_entry_t, absolute_period_requested, region_info_array, context_param_list);
  1353. }
  1354. }
  1355. #endif
  1356. return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
  1357. resampling_time_requested, options, dimensions,
  1358. rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested, context_param_list);
  1359. }