unit_test.c 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "common.h"
  3. static int check_number_printing(void) {
  4. struct {
  5. calculated_number n;
  6. const char *correct;
  7. } values[] = {
  8. { .n = 0, .correct = "0" },
  9. { .n = 0.0000001, .correct = "0.0000001" },
  10. { .n = 0.00000009, .correct = "0.0000001" },
  11. { .n = 0.000000001, .correct = "0" },
  12. { .n = 99.99999999999999999, .correct = "100" },
  13. { .n = -99.99999999999999999, .correct = "-100" },
  14. { .n = 123.4567890123456789, .correct = "123.456789" },
  15. { .n = 9999.9999999, .correct = "9999.9999999" },
  16. { .n = -9999.9999999, .correct = "-9999.9999999" },
  17. { .n = 0, .correct = NULL },
  18. };
  19. char netdata[50], system[50];
  20. int i, failed = 0;
  21. for(i = 0; values[i].correct ; i++) {
  22. print_calculated_number(netdata, values[i].n);
  23. snprintfz(system, 49, "%0.12" LONG_DOUBLE_MODIFIER, (LONG_DOUBLE)values[i].n);
  24. int ok = 1;
  25. if(strcmp(netdata, values[i].correct) != 0) {
  26. ok = 0;
  27. failed++;
  28. }
  29. fprintf(stderr, "'%s' (system) printed as '%s' (netdata): %s\n", system, netdata, ok?"OK":"FAILED");
  30. }
  31. if(failed) return 1;
  32. return 0;
  33. }
  34. static int check_rrdcalc_comparisons(void) {
  35. RRDCALC_STATUS a, b;
  36. // make sure calloc() sets the status to UNINITIALIZED
  37. memset(&a, 0, sizeof(RRDCALC_STATUS));
  38. if(a != RRDCALC_STATUS_UNINITIALIZED) {
  39. fprintf(stderr, "%s is not zero.\n", rrdcalc_status2string(RRDCALC_STATUS_UNINITIALIZED));
  40. return 1;
  41. }
  42. a = RRDCALC_STATUS_REMOVED;
  43. b = RRDCALC_STATUS_UNDEFINED;
  44. if(!(a < b)) {
  45. fprintf(stderr, "%s is not less than %s\n", rrdcalc_status2string(a), rrdcalc_status2string(b));
  46. return 1;
  47. }
  48. a = RRDCALC_STATUS_UNDEFINED;
  49. b = RRDCALC_STATUS_UNINITIALIZED;
  50. if(!(a < b)) {
  51. fprintf(stderr, "%s is not less than %s\n", rrdcalc_status2string(a), rrdcalc_status2string(b));
  52. return 1;
  53. }
  54. a = RRDCALC_STATUS_UNINITIALIZED;
  55. b = RRDCALC_STATUS_CLEAR;
  56. if(!(a < b)) {
  57. fprintf(stderr, "%s is not less than %s\n", rrdcalc_status2string(a), rrdcalc_status2string(b));
  58. return 1;
  59. }
  60. a = RRDCALC_STATUS_CLEAR;
  61. b = RRDCALC_STATUS_RAISED;
  62. if(!(a < b)) {
  63. fprintf(stderr, "%s is not less than %s\n", rrdcalc_status2string(a), rrdcalc_status2string(b));
  64. return 1;
  65. }
  66. a = RRDCALC_STATUS_RAISED;
  67. b = RRDCALC_STATUS_WARNING;
  68. if(!(a < b)) {
  69. fprintf(stderr, "%s is not less than %s\n", rrdcalc_status2string(a), rrdcalc_status2string(b));
  70. return 1;
  71. }
  72. a = RRDCALC_STATUS_WARNING;
  73. b = RRDCALC_STATUS_CRITICAL;
  74. if(!(a < b)) {
  75. fprintf(stderr, "%s is not less than %s\n", rrdcalc_status2string(a), rrdcalc_status2string(b));
  76. return 1;
  77. }
  78. fprintf(stderr, "RRDCALC_STATUSes are sortable.\n");
  79. return 0;
  80. }
  81. int check_storage_number(calculated_number n, int debug) {
  82. char buffer[100];
  83. uint32_t flags = SN_EXISTS;
  84. storage_number s = pack_storage_number(n, flags);
  85. calculated_number d = unpack_storage_number(s);
  86. if(!does_storage_number_exist(s)) {
  87. fprintf(stderr, "Exists flags missing for number " CALCULATED_NUMBER_FORMAT "!\n", n);
  88. return 5;
  89. }
  90. calculated_number ddiff = d - n;
  91. calculated_number dcdiff = ddiff * 100.0 / n;
  92. if(dcdiff < 0) dcdiff = -dcdiff;
  93. size_t len = (size_t)print_calculated_number(buffer, d);
  94. calculated_number p = str2ld(buffer, NULL);
  95. calculated_number pdiff = n - p;
  96. calculated_number pcdiff = pdiff * 100.0 / n;
  97. if(pcdiff < 0) pcdiff = -pcdiff;
  98. if(debug) {
  99. fprintf(stderr,
  100. CALCULATED_NUMBER_FORMAT " original\n"
  101. CALCULATED_NUMBER_FORMAT " packed and unpacked, (stored as 0x%08X, diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n"
  102. "%s printed after unpacked (%zu bytes)\n"
  103. CALCULATED_NUMBER_FORMAT " re-parsed from printed (diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n\n",
  104. n,
  105. d, s, ddiff, dcdiff,
  106. buffer, len,
  107. p, pdiff, pcdiff
  108. );
  109. if(len != strlen(buffer)) fprintf(stderr, "ERROR: printed number %s is reported to have length %zu but it has %zu\n", buffer, len, strlen(buffer));
  110. if(dcdiff > ACCURACY_LOSS_ACCEPTED_PERCENT)
  111. fprintf(stderr, "WARNING: packing number " CALCULATED_NUMBER_FORMAT " has accuracy loss " CALCULATED_NUMBER_FORMAT " %%\n", n, dcdiff);
  112. if(pcdiff > ACCURACY_LOSS_ACCEPTED_PERCENT)
  113. fprintf(stderr, "WARNING: re-parsing the packed, unpacked and printed number " CALCULATED_NUMBER_FORMAT " has accuracy loss " CALCULATED_NUMBER_FORMAT " %%\n", n, pcdiff);
  114. }
  115. if(len != strlen(buffer)) return 1;
  116. if(dcdiff > ACCURACY_LOSS_ACCEPTED_PERCENT) return 3;
  117. if(pcdiff > ACCURACY_LOSS_ACCEPTED_PERCENT) return 4;
  118. return 0;
  119. }
  120. calculated_number storage_number_min(calculated_number n) {
  121. calculated_number r = 1, last;
  122. do {
  123. last = n;
  124. n /= 2.0;
  125. storage_number t = pack_storage_number(n, SN_EXISTS);
  126. r = unpack_storage_number(t);
  127. } while(r != 0.0 && r != last);
  128. return last;
  129. }
  130. void benchmark_storage_number(int loop, int multiplier) {
  131. int i, j;
  132. calculated_number n, d;
  133. storage_number s;
  134. unsigned long long user, system, total, mine, their;
  135. calculated_number storage_number_positive_min = unpack_storage_number(STORAGE_NUMBER_POSITIVE_MIN_RAW);
  136. calculated_number storage_number_positive_max = unpack_storage_number(STORAGE_NUMBER_POSITIVE_MAX_RAW);
  137. char buffer[100];
  138. struct rusage now, last;
  139. fprintf(stderr, "\n\nBenchmarking %d numbers, please wait...\n\n", loop);
  140. // ------------------------------------------------------------------------
  141. fprintf(stderr, "SYSTEM LONG DOUBLE SIZE: %zu bytes\n", sizeof(calculated_number));
  142. fprintf(stderr, "NETDATA FLOATING POINT SIZE: %zu bytes\n", sizeof(storage_number));
  143. mine = (calculated_number)sizeof(storage_number) * (calculated_number)loop;
  144. their = (calculated_number)sizeof(calculated_number) * (calculated_number)loop;
  145. if(mine > their) {
  146. fprintf(stderr, "\nNETDATA NEEDS %0.2" LONG_DOUBLE_MODIFIER " TIMES MORE MEMORY. Sorry!\n", (LONG_DOUBLE)(mine / their));
  147. }
  148. else {
  149. fprintf(stderr, "\nNETDATA INTERNAL FLOATING POINT ARITHMETICS NEEDS %0.2" LONG_DOUBLE_MODIFIER " TIMES LESS MEMORY.\n", (LONG_DOUBLE)(their / mine));
  150. }
  151. fprintf(stderr, "\nNETDATA FLOATING POINT\n");
  152. fprintf(stderr, "MIN POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", unpack_storage_number(STORAGE_NUMBER_POSITIVE_MIN_RAW));
  153. fprintf(stderr, "MAX POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", unpack_storage_number(STORAGE_NUMBER_POSITIVE_MAX_RAW));
  154. fprintf(stderr, "MIN NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", unpack_storage_number(STORAGE_NUMBER_NEGATIVE_MIN_RAW));
  155. fprintf(stderr, "MAX NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", unpack_storage_number(STORAGE_NUMBER_NEGATIVE_MAX_RAW));
  156. fprintf(stderr, "Maximum accuracy loss accepted: " CALCULATED_NUMBER_FORMAT "%%\n\n\n", (calculated_number)ACCURACY_LOSS_ACCEPTED_PERCENT);
  157. // ------------------------------------------------------------------------
  158. fprintf(stderr, "INTERNAL LONG DOUBLE PRINTING: ");
  159. getrusage(RUSAGE_SELF, &last);
  160. // do the job
  161. for(j = 1; j < 11 ;j++) {
  162. n = storage_number_positive_min * j;
  163. for(i = 0; i < loop ;i++) {
  164. n *= multiplier;
  165. if(n > storage_number_positive_max) n = storage_number_positive_min;
  166. print_calculated_number(buffer, n);
  167. }
  168. }
  169. getrusage(RUSAGE_SELF, &now);
  170. user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
  171. system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
  172. total = user + system;
  173. mine = total;
  174. fprintf(stderr, "user %0.5" LONG_DOUBLE_MODIFIER", system %0.5" LONG_DOUBLE_MODIFIER ", total %0.5" LONG_DOUBLE_MODIFIER "\n", (LONG_DOUBLE)(user / 1000000.0), (LONG_DOUBLE)(system / 1000000.0), (LONG_DOUBLE)(total / 1000000.0));
  175. // ------------------------------------------------------------------------
  176. fprintf(stderr, "SYSTEM LONG DOUBLE PRINTING: ");
  177. getrusage(RUSAGE_SELF, &last);
  178. // do the job
  179. for(j = 1; j < 11 ;j++) {
  180. n = storage_number_positive_min * j;
  181. for(i = 0; i < loop ;i++) {
  182. n *= multiplier;
  183. if(n > storage_number_positive_max) n = storage_number_positive_min;
  184. snprintfz(buffer, 100, CALCULATED_NUMBER_FORMAT, n);
  185. }
  186. }
  187. getrusage(RUSAGE_SELF, &now);
  188. user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
  189. system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
  190. total = user + system;
  191. their = total;
  192. fprintf(stderr, "user %0.5" LONG_DOUBLE_MODIFIER ", system %0.5" LONG_DOUBLE_MODIFIER ", total %0.5" LONG_DOUBLE_MODIFIER "\n", (LONG_DOUBLE)(user / 1000000.0), (LONG_DOUBLE)(system / 1000000.0), (LONG_DOUBLE)(total / 1000000.0));
  193. if(mine > total) {
  194. fprintf(stderr, "NETDATA CODE IS SLOWER %0.2" LONG_DOUBLE_MODIFIER " %%\n", (LONG_DOUBLE)(mine * 100.0 / their - 100.0));
  195. }
  196. else {
  197. fprintf(stderr, "NETDATA CODE IS F A S T E R %0.2" LONG_DOUBLE_MODIFIER " %%\n", (LONG_DOUBLE)(their * 100.0 / mine - 100.0));
  198. }
  199. // ------------------------------------------------------------------------
  200. fprintf(stderr, "\nINTERNAL LONG DOUBLE PRINTING WITH PACK / UNPACK: ");
  201. getrusage(RUSAGE_SELF, &last);
  202. // do the job
  203. for(j = 1; j < 11 ;j++) {
  204. n = storage_number_positive_min * j;
  205. for(i = 0; i < loop ;i++) {
  206. n *= multiplier;
  207. if(n > storage_number_positive_max) n = storage_number_positive_min;
  208. s = pack_storage_number(n, SN_EXISTS);
  209. d = unpack_storage_number(s);
  210. print_calculated_number(buffer, d);
  211. }
  212. }
  213. getrusage(RUSAGE_SELF, &now);
  214. user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
  215. system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
  216. total = user + system;
  217. mine = total;
  218. fprintf(stderr, "user %0.5" LONG_DOUBLE_MODIFIER ", system %0.5" LONG_DOUBLE_MODIFIER ", total %0.5" LONG_DOUBLE_MODIFIER "\n", (LONG_DOUBLE)(user / 1000000.0), (LONG_DOUBLE)(system / 1000000.0), (LONG_DOUBLE)(total / 1000000.0));
  219. if(mine > their) {
  220. fprintf(stderr, "WITH PACKING UNPACKING NETDATA CODE IS SLOWER %0.2" LONG_DOUBLE_MODIFIER " %%\n", (LONG_DOUBLE)(mine * 100.0 / their - 100.0));
  221. }
  222. else {
  223. fprintf(stderr, "EVEN WITH PACKING AND UNPACKING, NETDATA CODE IS F A S T E R %0.2" LONG_DOUBLE_MODIFIER " %%\n", (LONG_DOUBLE)(their * 100.0 / mine - 100.0));
  224. }
  225. // ------------------------------------------------------------------------
  226. }
  227. static int check_storage_number_exists() {
  228. uint32_t flags;
  229. for(flags = 0; flags < 7 ; flags++) {
  230. if(get_storage_number_flags(flags << 24) != flags << 24) {
  231. fprintf(stderr, "Flag 0x%08x is not checked correctly. It became 0x%08x\n", flags << 24, get_storage_number_flags(flags << 24));
  232. return 1;
  233. }
  234. }
  235. flags = SN_EXISTS;
  236. calculated_number n = 0.0;
  237. storage_number s = pack_storage_number(n, flags);
  238. calculated_number d = unpack_storage_number(s);
  239. if(get_storage_number_flags(s) != flags) {
  240. fprintf(stderr, "Wrong flags. Given %08x, Got %08x!\n", flags, get_storage_number_flags(s));
  241. return 1;
  242. }
  243. if(n != d) {
  244. fprintf(stderr, "Wrong number returned. Expected " CALCULATED_NUMBER_FORMAT ", returned " CALCULATED_NUMBER_FORMAT "!\n", n, d);
  245. return 1;
  246. }
  247. return 0;
  248. }
  249. int unit_test_storage() {
  250. if(check_storage_number_exists()) return 0;
  251. calculated_number storage_number_positive_min = unpack_storage_number(STORAGE_NUMBER_POSITIVE_MIN_RAW);
  252. calculated_number storage_number_negative_max = unpack_storage_number(STORAGE_NUMBER_NEGATIVE_MAX_RAW);
  253. calculated_number c, a = 0;
  254. int i, j, g, r = 0;
  255. for(g = -1; g <= 1 ; g++) {
  256. a = 0;
  257. if(!g) continue;
  258. for(j = 0; j < 9 ;j++) {
  259. a += 0.0000001;
  260. c = a * g;
  261. for(i = 0; i < 21 ;i++, c *= 10) {
  262. if(c > 0 && c < storage_number_positive_min) continue;
  263. if(c < 0 && c > storage_number_negative_max) continue;
  264. if(check_storage_number(c, 1)) return 1;
  265. }
  266. }
  267. }
  268. // if(check_storage_number(858993459.1234567, 1)) return 1;
  269. benchmark_storage_number(1000000, 2);
  270. return r;
  271. }
  272. int unit_test_str2ld() {
  273. char *values[] = {
  274. "1.2345678", "-35.6", "0.00123", "23842384234234.2", ".1", "1.2e-10",
  275. "hello", "1wrong", "nan", "inf", NULL
  276. };
  277. int i;
  278. for(i = 0; values[i] ; i++) {
  279. char *e_mine = "hello", *e_sys = "world";
  280. LONG_DOUBLE mine = str2ld(values[i], &e_mine);
  281. LONG_DOUBLE sys = strtold(values[i], &e_sys);
  282. if(isnan(mine)) {
  283. if(!isnan(sys)) {
  284. fprintf(stderr, "Value '%s' is parsed as %" LONG_DOUBLE_MODIFIER ", but system believes it is %" LONG_DOUBLE_MODIFIER ".\n", values[i], mine, sys);
  285. return -1;
  286. }
  287. }
  288. else if(isinf(mine)) {
  289. if(!isinf(sys)) {
  290. fprintf(stderr, "Value '%s' is parsed as %" LONG_DOUBLE_MODIFIER ", but system believes it is %" LONG_DOUBLE_MODIFIER ".\n", values[i], mine, sys);
  291. return -1;
  292. }
  293. }
  294. else if(mine != sys && abs(mine-sys) > 0.000001) {
  295. fprintf(stderr, "Value '%s' is parsed as %" LONG_DOUBLE_MODIFIER ", but system believes it is %" LONG_DOUBLE_MODIFIER ", delta %" LONG_DOUBLE_MODIFIER ".\n", values[i], mine, sys, sys-mine);
  296. return -1;
  297. }
  298. if(e_mine != e_sys) {
  299. fprintf(stderr, "Value '%s' is parsed correctly, but endptr is not right\n", values[i]);
  300. return -1;
  301. }
  302. fprintf(stderr, "str2ld() parsed value '%s' exactly the same way with strtold(), returned %" LONG_DOUBLE_MODIFIER " vs %" LONG_DOUBLE_MODIFIER "\n", values[i], mine, sys);
  303. }
  304. return 0;
  305. }
  306. int unit_test_buffer() {
  307. BUFFER *wb = buffer_create(1);
  308. char string[2048 + 1];
  309. char final[9000 + 1];
  310. int i;
  311. for(i = 0; i < 2048; i++)
  312. string[i] = (char)((i % 24) + 'a');
  313. string[2048] = '\0';
  314. const char *fmt = "string1: %s\nstring2: %s\nstring3: %s\nstring4: %s";
  315. buffer_sprintf(wb, fmt, string, string, string, string);
  316. snprintfz(final, 9000, fmt, string, string, string, string);
  317. const char *s = buffer_tostring(wb);
  318. if(buffer_strlen(wb) != strlen(final) || strcmp(s, final) != 0) {
  319. fprintf(stderr, "\nbuffer_sprintf() is faulty.\n");
  320. fprintf(stderr, "\nstring : %s (length %zu)\n", string, strlen(string));
  321. fprintf(stderr, "\nbuffer : %s (length %zu)\n", s, buffer_strlen(wb));
  322. fprintf(stderr, "\nexpected: %s (length %zu)\n", final, strlen(final));
  323. buffer_free(wb);
  324. return -1;
  325. }
  326. fprintf(stderr, "buffer_sprintf() works as expected.\n");
  327. buffer_free(wb);
  328. return 0;
  329. }
  330. // --------------------------------------------------------------------------------------------------------------------
  331. struct feed_values {
  332. unsigned long long microseconds;
  333. collected_number value;
  334. };
  335. struct test {
  336. char name[100];
  337. char description[1024];
  338. int update_every;
  339. unsigned long long multiplier;
  340. unsigned long long divisor;
  341. RRD_ALGORITHM algorithm;
  342. unsigned long feed_entries;
  343. unsigned long result_entries;
  344. struct feed_values *feed;
  345. calculated_number *results;
  346. collected_number *feed2;
  347. calculated_number *results2;
  348. };
  349. // --------------------------------------------------------------------------------------------------------------------
  350. // test1
  351. // test absolute values stored
  352. struct feed_values test1_feed[] = {
  353. { 0, 10 },
  354. { 1000000, 20 },
  355. { 1000000, 30 },
  356. { 1000000, 40 },
  357. { 1000000, 50 },
  358. { 1000000, 60 },
  359. { 1000000, 70 },
  360. { 1000000, 80 },
  361. { 1000000, 90 },
  362. { 1000000, 100 },
  363. };
  364. calculated_number test1_results[] = {
  365. 20, 30, 40, 50, 60, 70, 80, 90, 100
  366. };
  367. struct test test1 = {
  368. "test1", // name
  369. "test absolute values stored at exactly second boundaries",
  370. 1, // update_every
  371. 1, // multiplier
  372. 1, // divisor
  373. RRD_ALGORITHM_ABSOLUTE, // algorithm
  374. 10, // feed entries
  375. 9, // result entries
  376. test1_feed, // feed
  377. test1_results, // results
  378. NULL, // feed2
  379. NULL // results2
  380. };
  381. // --------------------------------------------------------------------------------------------------------------------
  382. // test2
  383. // test absolute values stored in the middle of second boundaries
  384. struct feed_values test2_feed[] = {
  385. { 500000, 10 },
  386. { 1000000, 20 },
  387. { 1000000, 30 },
  388. { 1000000, 40 },
  389. { 1000000, 50 },
  390. { 1000000, 60 },
  391. { 1000000, 70 },
  392. { 1000000, 80 },
  393. { 1000000, 90 },
  394. { 1000000, 100 },
  395. };
  396. calculated_number test2_results[] = {
  397. 20, 30, 40, 50, 60, 70, 80, 90, 100
  398. };
  399. struct test test2 = {
  400. "test2", // name
  401. "test absolute values stored in the middle of second boundaries",
  402. 1, // update_every
  403. 1, // multiplier
  404. 1, // divisor
  405. RRD_ALGORITHM_ABSOLUTE, // algorithm
  406. 10, // feed entries
  407. 9, // result entries
  408. test2_feed, // feed
  409. test2_results, // results
  410. NULL, // feed2
  411. NULL // results2
  412. };
  413. // --------------------------------------------------------------------------------------------------------------------
  414. // test3
  415. struct feed_values test3_feed[] = {
  416. { 0, 10 },
  417. { 1000000, 20 },
  418. { 1000000, 30 },
  419. { 1000000, 40 },
  420. { 1000000, 50 },
  421. { 1000000, 60 },
  422. { 1000000, 70 },
  423. { 1000000, 80 },
  424. { 1000000, 90 },
  425. { 1000000, 100 },
  426. };
  427. calculated_number test3_results[] = {
  428. 10, 10, 10, 10, 10, 10, 10, 10, 10
  429. };
  430. struct test test3 = {
  431. "test3", // name
  432. "test incremental values stored at exactly second boundaries",
  433. 1, // update_every
  434. 1, // multiplier
  435. 1, // divisor
  436. RRD_ALGORITHM_INCREMENTAL, // algorithm
  437. 10, // feed entries
  438. 9, // result entries
  439. test3_feed, // feed
  440. test3_results, // results
  441. NULL, // feed2
  442. NULL // results2
  443. };
  444. // --------------------------------------------------------------------------------------------------------------------
  445. // test4
  446. struct feed_values test4_feed[] = {
  447. { 500000, 10 },
  448. { 1000000, 20 },
  449. { 1000000, 30 },
  450. { 1000000, 40 },
  451. { 1000000, 50 },
  452. { 1000000, 60 },
  453. { 1000000, 70 },
  454. { 1000000, 80 },
  455. { 1000000, 90 },
  456. { 1000000, 100 },
  457. };
  458. calculated_number test4_results[] = {
  459. 10, 10, 10, 10, 10, 10, 10, 10, 10
  460. };
  461. struct test test4 = {
  462. "test4", // name
  463. "test incremental values stored in the middle of second boundaries",
  464. 1, // update_every
  465. 1, // multiplier
  466. 1, // divisor
  467. RRD_ALGORITHM_INCREMENTAL, // algorithm
  468. 10, // feed entries
  469. 9, // result entries
  470. test4_feed, // feed
  471. test4_results, // results
  472. NULL, // feed2
  473. NULL // results2
  474. };
  475. // --------------------------------------------------------------------------------------------------------------------
  476. // test5 - 32 bit overflows
  477. struct feed_values test5_feed[] = {
  478. { 0, 0x00000000FFFFFFFFULL / 15 * 0 },
  479. { 1000000, 0x00000000FFFFFFFFULL / 15 * 7 },
  480. { 1000000, 0x00000000FFFFFFFFULL / 15 * 14 },
  481. { 1000000, 0x00000000FFFFFFFFULL / 15 * 0 },
  482. { 1000000, 0x00000000FFFFFFFFULL / 15 * 7 },
  483. { 1000000, 0x00000000FFFFFFFFULL / 15 * 14 },
  484. { 1000000, 0x00000000FFFFFFFFULL / 15 * 0 },
  485. { 1000000, 0x00000000FFFFFFFFULL / 15 * 7 },
  486. { 1000000, 0x00000000FFFFFFFFULL / 15 * 14 },
  487. { 1000000, 0x00000000FFFFFFFFULL / 15 * 0 },
  488. };
  489. calculated_number test5_results[] = {
  490. 0x00000000FFFFFFFFULL / 15 * 7,
  491. 0x00000000FFFFFFFFULL / 15 * 7,
  492. 0x00000000FFFFFFFFULL / 15,
  493. 0x00000000FFFFFFFFULL / 15 * 7,
  494. 0x00000000FFFFFFFFULL / 15 * 7,
  495. 0x00000000FFFFFFFFULL / 15,
  496. 0x00000000FFFFFFFFULL / 15 * 7,
  497. 0x00000000FFFFFFFFULL / 15 * 7,
  498. 0x00000000FFFFFFFFULL / 15,
  499. };
  500. struct test test5 = {
  501. "test5", // name
  502. "test 32-bit incremental values overflow",
  503. 1, // update_every
  504. 1, // multiplier
  505. 1, // divisor
  506. RRD_ALGORITHM_INCREMENTAL, // algorithm
  507. 10, // feed entries
  508. 9, // result entries
  509. test5_feed, // feed
  510. test5_results, // results
  511. NULL, // feed2
  512. NULL // results2
  513. };
  514. // --------------------------------------------------------------------------------------------------------------------
  515. // test5b - 64 bit overflows
  516. struct feed_values test5b_feed[] = {
  517. { 0, 0xFFFFFFFFFFFFFFFFULL / 15 * 0 },
  518. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 7 },
  519. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 14 },
  520. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 0 },
  521. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 7 },
  522. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 14 },
  523. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 0 },
  524. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 7 },
  525. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 14 },
  526. { 1000000, 0xFFFFFFFFFFFFFFFFULL / 15 * 0 },
  527. };
  528. calculated_number test5b_results[] = {
  529. 0xFFFFFFFFFFFFFFFFULL / 15 * 7,
  530. 0xFFFFFFFFFFFFFFFFULL / 15 * 7,
  531. 0xFFFFFFFFFFFFFFFFULL / 15,
  532. 0xFFFFFFFFFFFFFFFFULL / 15 * 7,
  533. 0xFFFFFFFFFFFFFFFFULL / 15 * 7,
  534. 0xFFFFFFFFFFFFFFFFULL / 15,
  535. 0xFFFFFFFFFFFFFFFFULL / 15 * 7,
  536. 0xFFFFFFFFFFFFFFFFULL / 15 * 7,
  537. 0xFFFFFFFFFFFFFFFFULL / 15,
  538. };
  539. struct test test5b = {
  540. "test5b", // name
  541. "test 64-bit incremental values overflow",
  542. 1, // update_every
  543. 1, // multiplier
  544. 1, // divisor
  545. RRD_ALGORITHM_INCREMENTAL, // algorithm
  546. 10, // feed entries
  547. 9, // result entries
  548. test5b_feed, // feed
  549. test5b_results, // results
  550. NULL, // feed2
  551. NULL // results2
  552. };
  553. // --------------------------------------------------------------------------------------------------------------------
  554. // test6
  555. struct feed_values test6_feed[] = {
  556. { 250000, 1000 },
  557. { 250000, 2000 },
  558. { 250000, 3000 },
  559. { 250000, 4000 },
  560. { 250000, 5000 },
  561. { 250000, 6000 },
  562. { 250000, 7000 },
  563. { 250000, 8000 },
  564. { 250000, 9000 },
  565. { 250000, 10000 },
  566. { 250000, 11000 },
  567. { 250000, 12000 },
  568. { 250000, 13000 },
  569. { 250000, 14000 },
  570. { 250000, 15000 },
  571. { 250000, 16000 },
  572. };
  573. calculated_number test6_results[] = {
  574. 4000, 4000, 4000, 4000
  575. };
  576. struct test test6 = {
  577. "test6", // name
  578. "test incremental values updated within the same second",
  579. 1, // update_every
  580. 1, // multiplier
  581. 1, // divisor
  582. RRD_ALGORITHM_INCREMENTAL, // algorithm
  583. 16, // feed entries
  584. 4, // result entries
  585. test6_feed, // feed
  586. test6_results, // results
  587. NULL, // feed2
  588. NULL // results2
  589. };
  590. // --------------------------------------------------------------------------------------------------------------------
  591. // test7
  592. struct feed_values test7_feed[] = {
  593. { 500000, 1000 },
  594. { 2000000, 2000 },
  595. { 2000000, 3000 },
  596. { 2000000, 4000 },
  597. { 2000000, 5000 },
  598. { 2000000, 6000 },
  599. { 2000000, 7000 },
  600. { 2000000, 8000 },
  601. { 2000000, 9000 },
  602. { 2000000, 10000 },
  603. };
  604. calculated_number test7_results[] = {
  605. 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
  606. };
  607. struct test test7 = {
  608. "test7", // name
  609. "test incremental values updated in long durations",
  610. 1, // update_every
  611. 1, // multiplier
  612. 1, // divisor
  613. RRD_ALGORITHM_INCREMENTAL, // algorithm
  614. 10, // feed entries
  615. 18, // result entries
  616. test7_feed, // feed
  617. test7_results, // results
  618. NULL, // feed2
  619. NULL // results2
  620. };
  621. // --------------------------------------------------------------------------------------------------------------------
  622. // test8
  623. struct feed_values test8_feed[] = {
  624. { 500000, 1000 },
  625. { 2000000, 2000 },
  626. { 2000000, 3000 },
  627. { 2000000, 4000 },
  628. { 2000000, 5000 },
  629. { 2000000, 6000 },
  630. };
  631. calculated_number test8_results[] = {
  632. 1250, 2000, 2250, 3000, 3250, 4000, 4250, 5000, 5250, 6000
  633. };
  634. struct test test8 = {
  635. "test8", // name
  636. "test absolute values updated in long durations",
  637. 1, // update_every
  638. 1, // multiplier
  639. 1, // divisor
  640. RRD_ALGORITHM_ABSOLUTE, // algorithm
  641. 6, // feed entries
  642. 10, // result entries
  643. test8_feed, // feed
  644. test8_results, // results
  645. NULL, // feed2
  646. NULL // results2
  647. };
  648. // --------------------------------------------------------------------------------------------------------------------
  649. // test9
  650. struct feed_values test9_feed[] = {
  651. { 250000, 1000 },
  652. { 250000, 2000 },
  653. { 250000, 3000 },
  654. { 250000, 4000 },
  655. { 250000, 5000 },
  656. { 250000, 6000 },
  657. { 250000, 7000 },
  658. { 250000, 8000 },
  659. { 250000, 9000 },
  660. { 250000, 10000 },
  661. { 250000, 11000 },
  662. { 250000, 12000 },
  663. { 250000, 13000 },
  664. { 250000, 14000 },
  665. { 250000, 15000 },
  666. { 250000, 16000 },
  667. };
  668. calculated_number test9_results[] = {
  669. 4000, 8000, 12000, 16000
  670. };
  671. struct test test9 = {
  672. "test9", // name
  673. "test absolute values updated within the same second",
  674. 1, // update_every
  675. 1, // multiplier
  676. 1, // divisor
  677. RRD_ALGORITHM_ABSOLUTE, // algorithm
  678. 16, // feed entries
  679. 4, // result entries
  680. test9_feed, // feed
  681. test9_results, // results
  682. NULL, // feed2
  683. NULL // results2
  684. };
  685. // --------------------------------------------------------------------------------------------------------------------
  686. // test10
  687. struct feed_values test10_feed[] = {
  688. { 500000, 1000 },
  689. { 600000, 1000 + 600 },
  690. { 200000, 1600 + 200 },
  691. { 1000000, 1800 + 1000 },
  692. { 200000, 2800 + 200 },
  693. { 2000000, 3000 + 2000 },
  694. { 600000, 5000 + 600 },
  695. { 400000, 5600 + 400 },
  696. { 900000, 6000 + 900 },
  697. { 1000000, 6900 + 1000 },
  698. };
  699. calculated_number test10_results[] = {
  700. 1000, 1000, 1000, 1000, 1000, 1000, 1000
  701. };
  702. struct test test10 = {
  703. "test10", // name
  704. "test incremental values updated in short and long durations",
  705. 1, // update_every
  706. 1, // multiplier
  707. 1, // divisor
  708. RRD_ALGORITHM_INCREMENTAL, // algorithm
  709. 10, // feed entries
  710. 7, // result entries
  711. test10_feed, // feed
  712. test10_results, // results
  713. NULL, // feed2
  714. NULL // results2
  715. };
  716. // --------------------------------------------------------------------------------------------------------------------
  717. // test11
  718. struct feed_values test11_feed[] = {
  719. { 0, 10 },
  720. { 1000000, 20 },
  721. { 1000000, 30 },
  722. { 1000000, 40 },
  723. { 1000000, 50 },
  724. { 1000000, 60 },
  725. { 1000000, 70 },
  726. { 1000000, 80 },
  727. { 1000000, 90 },
  728. { 1000000, 100 },
  729. };
  730. collected_number test11_feed2[] = {
  731. 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
  732. };
  733. calculated_number test11_results[] = {
  734. 50, 50, 50, 50, 50, 50, 50, 50, 50
  735. };
  736. calculated_number test11_results2[] = {
  737. 50, 50, 50, 50, 50, 50, 50, 50, 50
  738. };
  739. struct test test11 = {
  740. "test11", // name
  741. "test percentage-of-incremental-row with equal values",
  742. 1, // update_every
  743. 1, // multiplier
  744. 1, // divisor
  745. RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL, // algorithm
  746. 10, // feed entries
  747. 9, // result entries
  748. test11_feed, // feed
  749. test11_results, // results
  750. test11_feed2, // feed2
  751. test11_results2 // results2
  752. };
  753. // --------------------------------------------------------------------------------------------------------------------
  754. // test12
  755. struct feed_values test12_feed[] = {
  756. { 0, 10 },
  757. { 1000000, 20 },
  758. { 1000000, 30 },
  759. { 1000000, 40 },
  760. { 1000000, 50 },
  761. { 1000000, 60 },
  762. { 1000000, 70 },
  763. { 1000000, 80 },
  764. { 1000000, 90 },
  765. { 1000000, 100 },
  766. };
  767. collected_number test12_feed2[] = {
  768. 10*3, 20*3, 30*3, 40*3, 50*3, 60*3, 70*3, 80*3, 90*3, 100*3
  769. };
  770. calculated_number test12_results[] = {
  771. 25, 25, 25, 25, 25, 25, 25, 25, 25
  772. };
  773. calculated_number test12_results2[] = {
  774. 75, 75, 75, 75, 75, 75, 75, 75, 75
  775. };
  776. struct test test12 = {
  777. "test12", // name
  778. "test percentage-of-incremental-row with equal values",
  779. 1, // update_every
  780. 1, // multiplier
  781. 1, // divisor
  782. RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL, // algorithm
  783. 10, // feed entries
  784. 9, // result entries
  785. test12_feed, // feed
  786. test12_results, // results
  787. test12_feed2, // feed2
  788. test12_results2 // results2
  789. };
  790. // --------------------------------------------------------------------------------------------------------------------
  791. // test13
  792. struct feed_values test13_feed[] = {
  793. { 500000, 1000 },
  794. { 600000, 1000 + 600 },
  795. { 200000, 1600 + 200 },
  796. { 1000000, 1800 + 1000 },
  797. { 200000, 2800 + 200 },
  798. { 2000000, 3000 + 2000 },
  799. { 600000, 5000 + 600 },
  800. { 400000, 5600 + 400 },
  801. { 900000, 6000 + 900 },
  802. { 1000000, 6900 + 1000 },
  803. };
  804. calculated_number test13_results[] = {
  805. 83.3333300, 100, 100, 100, 100, 100, 100
  806. };
  807. struct test test13 = {
  808. "test13", // name
  809. "test incremental values updated in short and long durations",
  810. 1, // update_every
  811. 1, // multiplier
  812. 1, // divisor
  813. RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL, // algorithm
  814. 10, // feed entries
  815. 7, // result entries
  816. test13_feed, // feed
  817. test13_results, // results
  818. NULL, // feed2
  819. NULL // results2
  820. };
  821. // --------------------------------------------------------------------------------------------------------------------
  822. // test14
  823. struct feed_values test14_feed[] = {
  824. { 0, 0x015397dc42151c41ULL },
  825. { 13573000, 0x015397e612e3ff5dULL },
  826. { 29969000, 0x015397f905ecdaa8ULL },
  827. { 29958000, 0x0153980c2a6cb5e4ULL },
  828. { 30054000, 0x0153981f4032fb83ULL },
  829. { 34952000, 0x015398355efadaccULL },
  830. { 25046000, 0x01539845ba4b09f8ULL },
  831. { 29947000, 0x0153985948bf381dULL },
  832. { 30054000, 0x0153986c5b9c27e2ULL },
  833. { 29942000, 0x0153987f888982d0ULL },
  834. };
  835. calculated_number test14_results[] = {
  836. 23.1383300, 21.8515600, 21.8804600, 21.7788000, 22.0112200, 22.4386100, 22.0906100, 21.9150800
  837. };
  838. struct test test14 = {
  839. "test14", // name
  840. "issue #981 with real data",
  841. 30, // update_every
  842. 8, // multiplier
  843. 1000000000, // divisor
  844. RRD_ALGORITHM_INCREMENTAL, // algorithm
  845. 10, // feed entries
  846. 8, // result entries
  847. test14_feed, // feed
  848. test14_results, // results
  849. NULL, // feed2
  850. NULL // results2
  851. };
  852. struct feed_values test14b_feed[] = {
  853. { 0, 0 },
  854. { 13573000, 13573000 },
  855. { 29969000, 13573000 + 29969000 },
  856. { 29958000, 13573000 + 29969000 + 29958000 },
  857. { 30054000, 13573000 + 29969000 + 29958000 + 30054000 },
  858. { 34952000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 },
  859. { 25046000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 },
  860. { 29947000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 + 29947000 },
  861. { 30054000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 + 29947000 + 30054000 },
  862. { 29942000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 + 29947000 + 30054000 + 29942000 },
  863. };
  864. calculated_number test14b_results[] = {
  865. 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000
  866. };
  867. struct test test14b = {
  868. "test14b", // name
  869. "issue #981 with dummy data",
  870. 30, // update_every
  871. 1, // multiplier
  872. 1, // divisor
  873. RRD_ALGORITHM_INCREMENTAL, // algorithm
  874. 10, // feed entries
  875. 8, // result entries
  876. test14b_feed, // feed
  877. test14b_results, // results
  878. NULL, // feed2
  879. NULL // results2
  880. };
  881. struct feed_values test14c_feed[] = {
  882. { 29000000, 29000000 },
  883. { 1000000, 29000000 + 1000000 },
  884. { 30000000, 29000000 + 1000000 + 30000000 },
  885. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 },
  886. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 },
  887. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 },
  888. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
  889. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
  890. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
  891. { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
  892. };
  893. calculated_number test14c_results[] = {
  894. 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000
  895. };
  896. struct test test14c = {
  897. "test14c", // name
  898. "issue #981 with dummy data, checking for late start",
  899. 30, // update_every
  900. 1, // multiplier
  901. 1, // divisor
  902. RRD_ALGORITHM_INCREMENTAL, // algorithm
  903. 10, // feed entries
  904. 9, // result entries
  905. test14c_feed, // feed
  906. test14c_results, // results
  907. NULL, // feed2
  908. NULL // results2
  909. };
  910. // --------------------------------------------------------------------------------------------------------------------
  911. // test15
  912. struct feed_values test15_feed[] = {
  913. { 0, 1068066388 },
  914. { 1008752, 1068822698 },
  915. { 993809, 1069573072 },
  916. { 995911, 1070324135 },
  917. { 1014562, 1071078166 },
  918. { 994684, 1071831349 },
  919. { 993128, 1072235739 },
  920. { 1010332, 1072958871 },
  921. { 1003394, 1073707019 },
  922. { 995201, 1074460255 },
  923. };
  924. collected_number test15_feed2[] = {
  925. 178825286, 178825286, 178825286, 178825286, 178825498, 178825498, 179165652, 179202964, 179203282, 179204130
  926. };
  927. calculated_number test15_results[] = {
  928. 5857.4080000, 5898.4540000, 5891.6590000, 5806.3160000, 5914.2640000, 3202.2630000, 5589.6560000, 5822.5260000, 5911.7520000
  929. };
  930. calculated_number test15_results2[] = {
  931. 0.0000000, 0.0000000, 0.0024944, 1.6324779, 0.0212777, 2655.1890000, 290.5387000, 5.6733610, 6.5960220
  932. };
  933. struct test test15 = {
  934. "test15", // name
  935. "test incremental with 2 dimensions",
  936. 1, // update_every
  937. 8, // multiplier
  938. 1024, // divisor
  939. RRD_ALGORITHM_INCREMENTAL, // algorithm
  940. 10, // feed entries
  941. 9, // result entries
  942. test15_feed, // feed
  943. test15_results, // results
  944. test15_feed2, // feed2
  945. test15_results2 // results2
  946. };
  947. // --------------------------------------------------------------------------------------------------------------------
  948. int run_test(struct test *test)
  949. {
  950. fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
  951. default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC;
  952. default_rrd_update_every = test->update_every;
  953. char name[101];
  954. snprintfz(name, 100, "unittest-%s", test->name);
  955. // create the chart
  956. RRDSET *st = rrdset_create_localhost("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", "unittest", NULL, 1
  957. , test->update_every, RRDSET_TYPE_LINE);
  958. RRDDIM *rd = rrddim_add(st, "dim1", NULL, test->multiplier, test->divisor, test->algorithm);
  959. RRDDIM *rd2 = NULL;
  960. if(test->feed2)
  961. rd2 = rrddim_add(st, "dim2", NULL, test->multiplier, test->divisor, test->algorithm);
  962. rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
  963. // feed it with the test data
  964. time_t time_now = 0, time_start = now_realtime_sec();
  965. unsigned long c;
  966. collected_number last = 0;
  967. for(c = 0; c < test->feed_entries; c++) {
  968. if(debug_flags) fprintf(stderr, "\n\n");
  969. if(c) {
  970. time_now += test->feed[c].microseconds;
  971. fprintf(stderr, " > %s: feeding position %lu, after %0.3f seconds (%0.3f seconds from start), delta " CALCULATED_NUMBER_FORMAT ", rate " CALCULATED_NUMBER_FORMAT "\n",
  972. test->name, c+1,
  973. (float)test->feed[c].microseconds / 1000000.0,
  974. (float)time_now / 1000000.0,
  975. ((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor,
  976. (((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor) / (calculated_number)test->feed[c].microseconds * (calculated_number)1000000);
  977. // rrdset_next_usec_unfiltered(st, test->feed[c].microseconds);
  978. st->usec_since_last_update = test->feed[c].microseconds;
  979. }
  980. else {
  981. fprintf(stderr, " > %s: feeding position %lu\n", test->name, c+1);
  982. }
  983. fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd->name, test->feed[c].value);
  984. rrddim_set(st, "dim1", test->feed[c].value);
  985. last = test->feed[c].value;
  986. if(rd2) {
  987. fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd2->name, test->feed2[c]);
  988. rrddim_set(st, "dim2", test->feed2[c]);
  989. }
  990. rrdset_done(st);
  991. // align the first entry to second boundary
  992. if(!c) {
  993. fprintf(stderr, " > %s: fixing first collection time to be %llu microseconds to second boundary\n", test->name, test->feed[c].microseconds);
  994. rd->last_collected_time.tv_usec = st->last_collected_time.tv_usec = st->last_updated.tv_usec = test->feed[c].microseconds;
  995. // time_start = st->last_collected_time.tv_sec;
  996. }
  997. }
  998. // check the result
  999. int errors = 0;
  1000. if(st->counter != test->result_entries) {
  1001. fprintf(stderr, " %s stored %zu entries, but we were expecting %lu, ### E R R O R ###\n", test->name, st->counter, test->result_entries);
  1002. errors++;
  1003. }
  1004. unsigned long max = (st->counter < test->result_entries)?st->counter:test->result_entries;
  1005. for(c = 0 ; c < max ; c++) {
  1006. calculated_number v = unpack_storage_number(rd->values[c]);
  1007. calculated_number n = unpack_storage_number(pack_storage_number(test->results[c], SN_EXISTS));
  1008. int same = (calculated_number_round(v * 10000000.0) == calculated_number_round(n * 10000000.0))?1:0;
  1009. fprintf(stderr, " %s/%s: checking position %lu (at %lu secs), expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n",
  1010. test->name, rd->name, c+1,
  1011. (rrdset_first_entry_t(st) + c * st->update_every) - time_start,
  1012. n, v, (same)?"OK":"### E R R O R ###");
  1013. if(!same) errors++;
  1014. if(rd2) {
  1015. v = unpack_storage_number(rd2->values[c]);
  1016. n = test->results2[c];
  1017. same = (calculated_number_round(v * 10000000.0) == calculated_number_round(n * 10000000.0))?1:0;
  1018. fprintf(stderr, " %s/%s: checking position %lu (at %lu secs), expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n",
  1019. test->name, rd2->name, c+1,
  1020. (rrdset_first_entry_t(st) + c * st->update_every) - time_start,
  1021. n, v, (same)?"OK":"### E R R O R ###");
  1022. if(!same) errors++;
  1023. }
  1024. }
  1025. return errors;
  1026. }
  1027. static int test_variable_renames(void) {
  1028. fprintf(stderr, "Creating chart\n");
  1029. RRDSET *st = rrdset_create_localhost("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", "unittest", NULL, 1, 1, RRDSET_TYPE_LINE);
  1030. fprintf(stderr, "Created chart with id '%s', name '%s'\n", st->id, st->name);
  1031. fprintf(stderr, "Creating dimension DIM1\n");
  1032. RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
  1033. fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd1->id, rd1->name);
  1034. fprintf(stderr, "Creating dimension DIM2\n");
  1035. RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
  1036. fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd2->id, rd2->name);
  1037. fprintf(stderr, "Renaming chart to CHARTNAME1\n");
  1038. rrdset_set_name(st, "CHARTNAME1");
  1039. fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name);
  1040. fprintf(stderr, "Renaming chart to CHARTNAME2\n");
  1041. rrdset_set_name(st, "CHARTNAME2");
  1042. fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name);
  1043. fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME1\n");
  1044. rrddim_set_name(st, rd1, "DIM1NAME1");
  1045. fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name);
  1046. fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME2\n");
  1047. rrddim_set_name(st, rd1, "DIM1NAME2");
  1048. fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name);
  1049. fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME1\n");
  1050. rrddim_set_name(st, rd2, "DIM2NAME1");
  1051. fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name);
  1052. fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME2\n");
  1053. rrddim_set_name(st, rd2, "DIM2NAME2");
  1054. fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name);
  1055. BUFFER *buf = buffer_create(1);
  1056. health_api_v1_chart_variables2json(st, buf);
  1057. fprintf(stderr, "%s", buffer_tostring(buf));
  1058. buffer_free(buf);
  1059. return 1;
  1060. }
  1061. int check_strdupz_path_subpath() {
  1062. struct strdupz_path_subpath_checks {
  1063. const char *path;
  1064. const char *subpath;
  1065. const char *result;
  1066. } checks[] = {
  1067. { "", "", "." },
  1068. { "/", "", "/" },
  1069. { "/etc/netdata", "", "/etc/netdata" },
  1070. { "/etc/netdata///", "", "/etc/netdata" },
  1071. { "/etc/netdata///", "health.d", "/etc/netdata/health.d" },
  1072. { "/etc/netdata///", "///health.d", "/etc/netdata/health.d" },
  1073. { "/etc/netdata", "///health.d", "/etc/netdata/health.d" },
  1074. { "", "///health.d", "./health.d" },
  1075. { "/", "///health.d", "/health.d" },
  1076. // terminator
  1077. { NULL, NULL, NULL }
  1078. };
  1079. size_t i;
  1080. for(i = 0; checks[i].result ; i++) {
  1081. char *s = strdupz_path_subpath(checks[i].path, checks[i].subpath);
  1082. fprintf(stderr, "strdupz_path_subpath(\"%s\", \"%s\") = \"%s\": ", checks[i].path, checks[i].subpath, s);
  1083. if(!s || strcmp(s, checks[i].result) != 0) {
  1084. freez(s);
  1085. fprintf(stderr, "FAILED\n");
  1086. return 1;
  1087. }
  1088. else {
  1089. freez(s);
  1090. fprintf(stderr, "OK\n");
  1091. }
  1092. }
  1093. return 0;
  1094. }
  1095. int run_all_mockup_tests(void)
  1096. {
  1097. if(check_strdupz_path_subpath())
  1098. return 1;
  1099. if(check_number_printing())
  1100. return 1;
  1101. if(check_rrdcalc_comparisons())
  1102. return 1;
  1103. if(!test_variable_renames())
  1104. return 1;
  1105. if(run_test(&test1))
  1106. return 1;
  1107. if(run_test(&test2))
  1108. return 1;
  1109. if(run_test(&test3))
  1110. return 1;
  1111. if(run_test(&test4))
  1112. return 1;
  1113. if(run_test(&test5))
  1114. return 1;
  1115. if(run_test(&test5b))
  1116. return 1;
  1117. if(run_test(&test6))
  1118. return 1;
  1119. if(run_test(&test7))
  1120. return 1;
  1121. if(run_test(&test8))
  1122. return 1;
  1123. if(run_test(&test9))
  1124. return 1;
  1125. if(run_test(&test10))
  1126. return 1;
  1127. if(run_test(&test11))
  1128. return 1;
  1129. if(run_test(&test12))
  1130. return 1;
  1131. if(run_test(&test13))
  1132. return 1;
  1133. if(run_test(&test14))
  1134. return 1;
  1135. if(run_test(&test14b))
  1136. return 1;
  1137. if(run_test(&test14c))
  1138. return 1;
  1139. if(run_test(&test15))
  1140. return 1;
  1141. return 0;
  1142. }
  1143. int unit_test(long delay, long shift)
  1144. {
  1145. static int repeat = 0;
  1146. repeat++;
  1147. char name[101];
  1148. snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
  1149. //debug_flags = 0xffffffff;
  1150. default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC;
  1151. default_rrd_update_every = 1;
  1152. int do_abs = 1;
  1153. int do_inc = 1;
  1154. int do_abst = 0;
  1155. int do_absi = 0;
  1156. RRDSET *st = rrdset_create_localhost("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", "unittest", NULL, 1, 1
  1157. , RRDSET_TYPE_LINE);
  1158. rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
  1159. RRDDIM *rdabs = NULL;
  1160. RRDDIM *rdinc = NULL;
  1161. RRDDIM *rdabst = NULL;
  1162. RRDDIM *rdabsi = NULL;
  1163. if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRD_ALGORITHM_ABSOLUTE);
  1164. if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRD_ALGORITHM_INCREMENTAL);
  1165. if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL);
  1166. if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
  1167. long increment = 1000;
  1168. collected_number i = 0;
  1169. unsigned long c, dimensions = 0;
  1170. RRDDIM *rd;
  1171. for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
  1172. for(c = 0; c < 20 ;c++) {
  1173. i += increment;
  1174. fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i);
  1175. if(c) {
  1176. // rrdset_next_usec_unfiltered(st, delay);
  1177. st->usec_since_last_update = delay;
  1178. }
  1179. if(do_abs) rrddim_set(st, "absolute", i);
  1180. if(do_inc) rrddim_set(st, "incremental", i);
  1181. if(do_abst) rrddim_set(st, "percentage-of-absolute-row", i);
  1182. if(do_absi) rrddim_set(st, "percentage-of-incremental-row", i);
  1183. if(!c) {
  1184. now_realtime_timeval(&st->last_collected_time);
  1185. st->last_collected_time.tv_usec = shift;
  1186. }
  1187. // prevent it from deleting the dimensions
  1188. for(rd = st->dimensions ; rd ; rd = rd->next)
  1189. rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec;
  1190. rrdset_done(st);
  1191. }
  1192. unsigned long oincrement = increment;
  1193. increment = increment * st->update_every * 1000000 / delay;
  1194. fprintf(stderr, "\n\nORIGINAL INCREMENT: %lu, INCREMENT %ld, DELAY %ld, SHIFT %ld\n", oincrement * 10, increment * 10, delay, shift);
  1195. int ret = 0;
  1196. storage_number sn;
  1197. calculated_number cn, v;
  1198. for(c = 0 ; c < st->counter ; c++) {
  1199. fprintf(stderr, "\nPOSITION: c = %lu, EXPECTED VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10);
  1200. for(rd = st->dimensions ; rd ; rd = rd->next) {
  1201. sn = rd->values[c];
  1202. cn = unpack_storage_number(sn);
  1203. fprintf(stderr, "\t %s " CALCULATED_NUMBER_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ") -> ", rd->id, cn, sn);
  1204. if(rd == rdabs) v =
  1205. ( oincrement
  1206. // + (increment * (1000000 - shift) / 1000000)
  1207. + (c + 1) * increment
  1208. );
  1209. else if(rd == rdinc) v = (c?(increment):(increment * (1000000 - shift) / 1000000));
  1210. else if(rd == rdabst) v = oincrement / dimensions / 10;
  1211. else if(rd == rdabsi) v = oincrement / dimensions / 10;
  1212. else v = 0;
  1213. if(v == cn) fprintf(stderr, "passed.\n");
  1214. else {
  1215. fprintf(stderr, "ERROR! (expected " CALCULATED_NUMBER_FORMAT ")\n", v);
  1216. ret = 1;
  1217. }
  1218. }
  1219. }
  1220. if(ret)
  1221. fprintf(stderr, "\n\nUNIT TEST(%ld, %ld) FAILED\n\n", delay, shift);
  1222. return ret;
  1223. }
  1224. #ifdef ENABLE_DBENGINE
  1225. static inline void rrddim_set_by_pointer_fake_time(RRDDIM *rd, collected_number value, time_t now)
  1226. {
  1227. rd->last_collected_time.tv_sec = now;
  1228. rd->last_collected_time.tv_usec = 0;
  1229. rd->collected_value = value;
  1230. rd->updated = 1;
  1231. rd->collections_counter++;
  1232. collected_number v = (value >= 0) ? value : -value;
  1233. if(unlikely(v > rd->collected_value_max)) rd->collected_value_max = v;
  1234. }
  1235. static RRDHOST *dbengine_rrdhost_find_or_create(char *name)
  1236. {
  1237. /* We don't want to drop metrics when generating load, we prefer to block data generation itself */
  1238. rrdeng_drop_metrics_under_page_cache_pressure = 0;
  1239. return rrdhost_find_or_create(
  1240. name
  1241. , name
  1242. , name
  1243. , os_type
  1244. , netdata_configured_timezone
  1245. , config_get(CONFIG_SECTION_BACKEND, "host tags", "")
  1246. , program_name
  1247. , program_version
  1248. , default_rrd_update_every
  1249. , default_rrd_history_entries
  1250. , RRD_MEMORY_MODE_DBENGINE
  1251. , default_health_enabled
  1252. , default_rrdpush_enabled
  1253. , default_rrdpush_destination
  1254. , default_rrdpush_api_key
  1255. , default_rrdpush_send_charts_matching
  1256. , NULL
  1257. );
  1258. }
  1259. // costants for test_dbengine
  1260. static const int CHARTS = 64;
  1261. static const int DIMS = 16; // That gives us 64 * 16 = 1024 metrics
  1262. #define REGIONS (3) // 3 regions of update_every
  1263. // first region update_every is 2, second is 3, third is 1
  1264. static const int REGION_UPDATE_EVERY[REGIONS] = {2, 3, 1};
  1265. static const int REGION_POINTS[REGIONS] = {
  1266. 16384, // This produces 64MiB of metric data for the first region: update_every = 2
  1267. 16384, // This produces 64MiB of metric data for the second region: update_every = 3
  1268. 16384, // This produces 64MiB of metric data for the third region: update_every = 1
  1269. };
  1270. static const int QUERY_BATCH = 4096;
  1271. static void test_dbengine_create_charts(RRDHOST *host, RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS],
  1272. int update_every)
  1273. {
  1274. int i, j;
  1275. char name[101];
  1276. for (i = 0 ; i < CHARTS ; ++i) {
  1277. snprintfz(name, 100, "dbengine-chart-%d", i);
  1278. // create the chart
  1279. st[i] = rrdset_create(host, "netdata", name, name, "netdata", NULL, "Unit Testing", "a value", "unittest",
  1280. NULL, 1, update_every, RRDSET_TYPE_LINE);
  1281. rrdset_flag_set(st[i], RRDSET_FLAG_DEBUG);
  1282. rrdset_flag_set(st[i], RRDSET_FLAG_STORE_FIRST);
  1283. for (j = 0 ; j < DIMS ; ++j) {
  1284. snprintfz(name, 100, "dim-%d", j);
  1285. rd[i][j] = rrddim_add(st[i], name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  1286. }
  1287. }
  1288. // Initialize DB with the very first entries
  1289. for (i = 0 ; i < CHARTS ; ++i) {
  1290. for (j = 0 ; j < DIMS ; ++j) {
  1291. rd[i][j]->last_collected_time.tv_sec =
  1292. st[i]->last_collected_time.tv_sec = st[i]->last_updated.tv_sec = 2 * API_RELATIVE_TIME_MAX - 1;
  1293. rd[i][j]->last_collected_time.tv_usec =
  1294. st[i]->last_collected_time.tv_usec = st[i]->last_updated.tv_usec = 0;
  1295. }
  1296. }
  1297. for (i = 0 ; i < CHARTS ; ++i) {
  1298. st[i]->usec_since_last_update = USEC_PER_SEC;
  1299. for (j = 0; j < DIMS; ++j) {
  1300. rrddim_set_by_pointer_fake_time(rd[i][j], 69, 2 * API_RELATIVE_TIME_MAX); // set first value to 69
  1301. }
  1302. rrdset_done(st[i]);
  1303. }
  1304. // Fluh pages for subsequent real values
  1305. for (i = 0 ; i < CHARTS ; ++i) {
  1306. for (j = 0; j < DIMS; ++j) {
  1307. rrdeng_store_metric_flush_current_page(rd[i][j]);
  1308. }
  1309. }
  1310. }
  1311. // Feeds the database region with test data, returns last timestamp of region
  1312. static time_t test_dbengine_create_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS],
  1313. int current_region, time_t time_start)
  1314. {
  1315. time_t time_now;
  1316. int i, j, c, update_every;
  1317. collected_number next;
  1318. update_every = REGION_UPDATE_EVERY[current_region];
  1319. time_now = time_start + update_every;
  1320. // feed it with the test data
  1321. for (i = 0 ; i < CHARTS ; ++i) {
  1322. for (j = 0 ; j < DIMS ; ++j) {
  1323. rd[i][j]->last_collected_time.tv_sec =
  1324. st[i]->last_collected_time.tv_sec = st[i]->last_updated.tv_sec = time_now;
  1325. rd[i][j]->last_collected_time.tv_usec =
  1326. st[i]->last_collected_time.tv_usec = st[i]->last_updated.tv_usec = 0;
  1327. }
  1328. }
  1329. for (c = 0; c < REGION_POINTS[current_region] ; ++c) {
  1330. time_now += update_every; // time_now = start + (c + 2) * update_every
  1331. for (i = 0 ; i < CHARTS ; ++i) {
  1332. st[i]->usec_since_last_update = USEC_PER_SEC * update_every;
  1333. for (j = 0; j < DIMS; ++j) {
  1334. next = ((collected_number)i * DIMS) * REGION_POINTS[current_region] +
  1335. j * REGION_POINTS[current_region] + c;
  1336. rrddim_set_by_pointer_fake_time(rd[i][j], next, time_now);
  1337. }
  1338. rrdset_done(st[i]);
  1339. }
  1340. }
  1341. return time_now; //time_end
  1342. }
  1343. // Checks the metric data for the given region, returns number of errors
  1344. static int test_dbengine_check_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS],
  1345. int current_region, time_t time_start)
  1346. {
  1347. uint8_t same;
  1348. time_t time_now, time_retrieved;
  1349. int i, j, k, c, errors, update_every;
  1350. collected_number last;
  1351. calculated_number value, expected;
  1352. storage_number n;
  1353. struct rrddim_query_handle handle;
  1354. update_every = REGION_UPDATE_EVERY[current_region];
  1355. errors = 0;
  1356. // check the result
  1357. for (c = 0; c < REGION_POINTS[current_region] ; c += QUERY_BATCH) {
  1358. time_now = time_start + (c + 2) * update_every;
  1359. for (i = 0 ; i < CHARTS ; ++i) {
  1360. for (j = 0; j < DIMS; ++j) {
  1361. rd[i][j]->state->query_ops.init(rd[i][j], &handle, time_now, time_now + QUERY_BATCH * update_every);
  1362. for (k = 0; k < QUERY_BATCH; ++k) {
  1363. last = ((collected_number)i * DIMS) * REGION_POINTS[current_region] +
  1364. j * REGION_POINTS[current_region] + c + k;
  1365. expected = unpack_storage_number(pack_storage_number((calculated_number)last, SN_EXISTS));
  1366. n = rd[i][j]->state->query_ops.next_metric(&handle, &time_retrieved);
  1367. value = unpack_storage_number(n);
  1368. same = (calculated_number_round(value) == calculated_number_round(expected)) ? 1 : 0;
  1369. if(!same) {
  1370. fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, expecting value "
  1371. CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", ### E R R O R ###\n",
  1372. st[i]->name, rd[i][j]->name, (unsigned long)time_now + k * update_every, expected, value);
  1373. errors++;
  1374. }
  1375. if(time_retrieved != time_now + k * update_every) {
  1376. fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, found timestamp %lu ### E R R O R ###\n",
  1377. st[i]->name, rd[i][j]->name, (unsigned long)time_now + k * update_every, (unsigned long)time_retrieved);
  1378. errors++;
  1379. }
  1380. }
  1381. rd[i][j]->state->query_ops.finalize(&handle);
  1382. }
  1383. }
  1384. }
  1385. return errors;
  1386. }
  1387. // Check rrdr transformations
  1388. static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS],
  1389. int current_region, time_t time_start, time_t time_end)
  1390. {
  1391. uint8_t same;
  1392. time_t time_now, time_retrieved;
  1393. int i, j, errors, update_every;
  1394. long c;
  1395. collected_number last;
  1396. calculated_number value, expected;
  1397. errors = 0;
  1398. update_every = REGION_UPDATE_EVERY[current_region];
  1399. long points = (time_end - time_start) / update_every - 1;
  1400. for (i = 0 ; i < CHARTS ; ++i) {
  1401. RRDR *r = rrd2rrdr(st[i], points, time_start + update_every, time_end, RRDR_GROUPING_AVERAGE, 0, 0, NULL);
  1402. if (!r) {
  1403. fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name);
  1404. return ++errors;
  1405. } else {
  1406. assert(r->st == st[i]);
  1407. for (c = 0; c != rrdr_rows(r) ; ++c) {
  1408. RRDDIM *d;
  1409. time_now = time_start + (c + 2) * update_every;
  1410. time_retrieved = r->t[c];
  1411. // for each dimension
  1412. for (j = 0, d = r->st->dimensions ; d && j < r->d ; ++j, d = d->next) {
  1413. calculated_number *cn = &r->v[ c * r->d ];
  1414. value = cn[j];
  1415. assert(rd[i][j] == d);
  1416. last = i * DIMS * REGION_POINTS[current_region] + j * REGION_POINTS[current_region] + c;
  1417. expected = unpack_storage_number(pack_storage_number((calculated_number)last, SN_EXISTS));
  1418. same = (calculated_number_round(value) == calculated_number_round(expected)) ? 1 : 0;
  1419. if(!same) {
  1420. fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, expecting value "
  1421. CALCULATED_NUMBER_FORMAT ", RRDR found " CALCULATED_NUMBER_FORMAT ", ### E R R O R ###\n",
  1422. st[i]->name, rd[i][j]->name, (unsigned long)time_now, expected, value);
  1423. errors++;
  1424. }
  1425. if(time_retrieved != time_now) {
  1426. fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, found RRDR timestamp %lu ### E R R O R ###\n",
  1427. st[i]->name, rd[i][j]->name, (unsigned long)time_now, (unsigned long)time_retrieved);
  1428. errors++;
  1429. }
  1430. }
  1431. }
  1432. rrdr_free(r);
  1433. }
  1434. }
  1435. return errors;
  1436. }
  1437. int test_dbengine(void)
  1438. {
  1439. int i, j, errors, update_every, current_region;
  1440. RRDHOST *host = NULL;
  1441. RRDSET *st[CHARTS];
  1442. RRDDIM *rd[CHARTS][DIMS];
  1443. time_t time_start[REGIONS], time_end[REGIONS];
  1444. error_log_limit_unlimited();
  1445. fprintf(stderr, "\nRunning DB-engine test\n");
  1446. default_rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE;
  1447. fprintf(stderr, "Initializing localhost with hostname 'unittest-dbengine'");
  1448. host = dbengine_rrdhost_find_or_create("unittest-dbengine");
  1449. if (NULL == host)
  1450. return 1;
  1451. current_region = 0; // this is the first region of data
  1452. update_every = REGION_UPDATE_EVERY[current_region]; // set data collection frequency to 2 seconds
  1453. test_dbengine_create_charts(host, st, rd, update_every);
  1454. time_start[current_region] = 2 * API_RELATIVE_TIME_MAX;
  1455. time_end[current_region] = test_dbengine_create_metrics(st,rd, current_region, time_start[current_region]);
  1456. errors = test_dbengine_check_metrics(st, rd, current_region, time_start[current_region]);
  1457. if (errors)
  1458. goto error_out;
  1459. current_region = 1; //this is the second region of data
  1460. update_every = REGION_UPDATE_EVERY[current_region]; // set data collection frequency to 3 seconds
  1461. // Align pages for frequency change
  1462. for (i = 0 ; i < CHARTS ; ++i) {
  1463. st[i]->update_every = update_every;
  1464. for (j = 0; j < DIMS; ++j) {
  1465. rrdeng_store_metric_flush_current_page(rd[i][j]);
  1466. }
  1467. }
  1468. time_start[current_region] = time_end[current_region - 1] + update_every;
  1469. if (0 != time_start[current_region] % update_every) // align to update_every
  1470. time_start[current_region] += update_every - time_start[current_region] % update_every;
  1471. time_end[current_region] = test_dbengine_create_metrics(st,rd, current_region, time_start[current_region]);
  1472. errors = test_dbengine_check_metrics(st, rd, current_region, time_start[current_region]);
  1473. if (errors)
  1474. goto error_out;
  1475. current_region = 2; //this is the third region of data
  1476. update_every = REGION_UPDATE_EVERY[current_region]; // set data collection frequency to 1 seconds
  1477. // Align pages for frequency change
  1478. for (i = 0 ; i < CHARTS ; ++i) {
  1479. st[i]->update_every = update_every;
  1480. for (j = 0; j < DIMS; ++j) {
  1481. rrdeng_store_metric_flush_current_page(rd[i][j]);
  1482. }
  1483. }
  1484. time_start[current_region] = time_end[current_region - 1] + update_every;
  1485. if (0 != time_start[current_region] % update_every) // align to update_every
  1486. time_start[current_region] += update_every - time_start[current_region] % update_every;
  1487. time_end[current_region] = test_dbengine_create_metrics(st,rd, current_region, time_start[current_region]);
  1488. errors = test_dbengine_check_metrics(st, rd, current_region, time_start[current_region]);
  1489. if (errors)
  1490. goto error_out;
  1491. for (current_region = 0 ; current_region < REGIONS ; ++current_region) {
  1492. errors = test_dbengine_check_rrdr(st, rd, current_region, time_start[current_region], time_end[current_region]);
  1493. if (errors)
  1494. goto error_out;
  1495. }
  1496. current_region = 1;
  1497. update_every = REGION_UPDATE_EVERY[current_region]; // use the maximum update_every = 3
  1498. errors = 0;
  1499. long points = (time_end[REGIONS - 1] - time_start[0]) / update_every - 1; // cover all time regions with RRDR
  1500. long point_offset = (time_start[current_region] - time_start[0]) / update_every;
  1501. for (i = 0 ; i < CHARTS ; ++i) {
  1502. RRDR *r = rrd2rrdr(st[i], points, time_start[0] + update_every, time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, 0, NULL);
  1503. if (!r) {
  1504. fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name);
  1505. ++errors;
  1506. } else {
  1507. long c;
  1508. assert(r->st == st[i]);
  1509. // test current region values only, since they must be left unchanged
  1510. for (c = point_offset ; c < point_offset + rrdr_rows(r) / REGIONS / 2 ; ++c) {
  1511. RRDDIM *d;
  1512. time_t time_now = time_start[current_region] + (c - point_offset + 2) * update_every;
  1513. time_t time_retrieved = r->t[c];
  1514. // for each dimension
  1515. for(j = 0, d = r->st->dimensions ; d && j < r->d ; ++j, d = d->next) {
  1516. calculated_number *cn = &r->v[ c * r->d ];
  1517. calculated_number value = cn[j];
  1518. assert(rd[i][j] == d);
  1519. collected_number last = i * DIMS * REGION_POINTS[current_region] + j * REGION_POINTS[current_region] + c - point_offset;
  1520. calculated_number expected = unpack_storage_number(pack_storage_number((calculated_number)last, SN_EXISTS));
  1521. uint8_t same = (calculated_number_round(value) == calculated_number_round(expected)) ? 1 : 0;
  1522. if(!same) {
  1523. fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, expecting value "
  1524. CALCULATED_NUMBER_FORMAT ", RRDR found " CALCULATED_NUMBER_FORMAT ", ### E R R O R ###\n",
  1525. st[i]->name, rd[i][j]->name, (unsigned long)time_now, expected, value);
  1526. errors++;
  1527. }
  1528. if(time_retrieved != time_now) {
  1529. fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, found RRDR timestamp %lu ### E R R O R ###\n",
  1530. st[i]->name, rd[i][j]->name, (unsigned long)time_now, (unsigned long)time_retrieved);
  1531. errors++;
  1532. }
  1533. }
  1534. }
  1535. rrdr_free(r);
  1536. }
  1537. }
  1538. error_out:
  1539. rrd_wrlock();
  1540. rrdeng_prepare_exit(host->rrdeng_ctx);
  1541. rrdhost_delete_charts(host);
  1542. rrdeng_exit(host->rrdeng_ctx);
  1543. rrd_unlock();
  1544. return errors;
  1545. }
  1546. struct dbengine_chart_thread {
  1547. uv_thread_t thread;
  1548. RRDHOST *host;
  1549. char *chartname; /* Will be prefixed by type, e.g. "example_local1.", "example_local2." etc */
  1550. unsigned dset_charts; /* number of charts */
  1551. unsigned dset_dims; /* dimensions per chart */
  1552. unsigned chart_i; /* current chart offset */
  1553. time_t time_present; /* current virtual time of the benchmark */
  1554. volatile time_t time_max; /* latest timestamp of stored values */
  1555. unsigned history_seconds; /* how far back in the past to go */
  1556. volatile long done; /* initialize to 0, set to 1 to stop thread */
  1557. struct completion charts_initialized;
  1558. unsigned long errors, stored_metrics_nr; /* statistics */
  1559. RRDSET *st;
  1560. RRDDIM *rd[]; /* dset_dims elements */
  1561. };
  1562. collected_number generate_dbengine_chart_value(int chart_i, int dim_i, time_t time_current)
  1563. {
  1564. collected_number value;
  1565. value = ((collected_number)time_current) * (chart_i + 1);
  1566. value += ((collected_number)time_current) * (dim_i + 1);
  1567. value %= 1024LLU;
  1568. return value;
  1569. }
  1570. static void generate_dbengine_chart(void *arg)
  1571. {
  1572. struct dbengine_chart_thread *thread_info = (struct dbengine_chart_thread *)arg;
  1573. RRDHOST *host = thread_info->host;
  1574. char *chartname = thread_info->chartname;
  1575. const unsigned DSET_DIMS = thread_info->dset_dims;
  1576. unsigned history_seconds = thread_info->history_seconds;
  1577. time_t time_present = thread_info->time_present;
  1578. unsigned j, update_every = 1;
  1579. RRDSET *st;
  1580. RRDDIM *rd[DSET_DIMS];
  1581. char name[RRD_ID_LENGTH_MAX + 1];
  1582. time_t time_current;
  1583. // create the chart
  1584. snprintfz(name, RRD_ID_LENGTH_MAX, "example_local%u", thread_info->chart_i + 1);
  1585. thread_info->st = st = rrdset_create(host, name, chartname, chartname, "example", NULL, chartname, chartname,
  1586. chartname, NULL, 1, update_every, RRDSET_TYPE_LINE);
  1587. for (j = 0 ; j < DSET_DIMS ; ++j) {
  1588. snprintfz(name, RRD_ID_LENGTH_MAX, "%s%u", chartname, j + 1);
  1589. thread_info->rd[j] = rd[j] = rrddim_add(st, name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  1590. }
  1591. complete(&thread_info->charts_initialized);
  1592. // feed it with the test data
  1593. time_current = time_present - history_seconds;
  1594. for (j = 0 ; j < DSET_DIMS ; ++j) {
  1595. rd[j]->last_collected_time.tv_sec =
  1596. st->last_collected_time.tv_sec = st->last_updated.tv_sec = time_current - update_every;
  1597. rd[j]->last_collected_time.tv_usec =
  1598. st->last_collected_time.tv_usec = st->last_updated.tv_usec = 0;
  1599. }
  1600. for( ; !thread_info->done && time_current < time_present ; time_current += update_every) {
  1601. st->usec_since_last_update = USEC_PER_SEC * update_every;
  1602. for (j = 0; j < DSET_DIMS; ++j) {
  1603. collected_number value;
  1604. value = generate_dbengine_chart_value(thread_info->chart_i, j, time_current);
  1605. rrddim_set_by_pointer_fake_time(rd[j], value, time_current);
  1606. ++thread_info->stored_metrics_nr;
  1607. }
  1608. rrdset_done(st);
  1609. thread_info->time_max = time_current;
  1610. }
  1611. for (j = 0; j < DSET_DIMS; ++j) {
  1612. rrdeng_store_metric_finalize(rd[j]);
  1613. }
  1614. }
  1615. void generate_dbengine_dataset(unsigned history_seconds)
  1616. {
  1617. const int DSET_CHARTS = 16;
  1618. const int DSET_DIMS = 128;
  1619. const uint64_t EXPECTED_COMPRESSION_RATIO = 20;
  1620. RRDHOST *host = NULL;
  1621. struct dbengine_chart_thread **thread_info;
  1622. int i;
  1623. time_t time_present;
  1624. default_rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE;
  1625. default_rrdeng_page_cache_mb = 128;
  1626. // Worst case for uncompressible data
  1627. default_rrdeng_disk_quota_mb = (((uint64_t)DSET_DIMS * DSET_CHARTS) * sizeof(storage_number) * history_seconds) /
  1628. (1024 * 1024);
  1629. default_rrdeng_disk_quota_mb -= default_rrdeng_disk_quota_mb * EXPECTED_COMPRESSION_RATIO / 100;
  1630. error_log_limit_unlimited();
  1631. fprintf(stderr, "Initializing localhost with hostname 'dbengine-dataset'");
  1632. host = dbengine_rrdhost_find_or_create("dbengine-dataset");
  1633. if (NULL == host)
  1634. return;
  1635. thread_info = mallocz(sizeof(*thread_info) * DSET_CHARTS);
  1636. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1637. thread_info[i] = mallocz(sizeof(*thread_info[i]) + sizeof(RRDDIM *) * DSET_DIMS);
  1638. }
  1639. fprintf(stderr, "\nRunning DB-engine workload generator\n");
  1640. time_present = now_realtime_sec();
  1641. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1642. thread_info[i]->host = host;
  1643. thread_info[i]->chartname = "random";
  1644. thread_info[i]->dset_charts = DSET_CHARTS;
  1645. thread_info[i]->chart_i = i;
  1646. thread_info[i]->dset_dims = DSET_DIMS;
  1647. thread_info[i]->history_seconds = history_seconds;
  1648. thread_info[i]->time_present = time_present;
  1649. thread_info[i]->time_max = 0;
  1650. thread_info[i]->done = 0;
  1651. init_completion(&thread_info[i]->charts_initialized);
  1652. assert(0 == uv_thread_create(&thread_info[i]->thread, generate_dbengine_chart, thread_info[i]));
  1653. wait_for_completion(&thread_info[i]->charts_initialized);
  1654. destroy_completion(&thread_info[i]->charts_initialized);
  1655. }
  1656. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1657. assert(0 == uv_thread_join(&thread_info[i]->thread));
  1658. }
  1659. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1660. freez(thread_info[i]);
  1661. }
  1662. freez(thread_info);
  1663. rrd_wrlock();
  1664. rrdhost_free(host);
  1665. rrd_unlock();
  1666. }
  1667. struct dbengine_query_thread {
  1668. uv_thread_t thread;
  1669. RRDHOST *host;
  1670. char *chartname; /* Will be prefixed by type, e.g. "example_local1.", "example_local2." etc */
  1671. unsigned dset_charts; /* number of charts */
  1672. unsigned dset_dims; /* dimensions per chart */
  1673. time_t time_present; /* current virtual time of the benchmark */
  1674. unsigned history_seconds; /* how far back in the past to go */
  1675. volatile long done; /* initialize to 0, set to 1 to stop thread */
  1676. unsigned long errors, queries_nr, queried_metrics_nr; /* statistics */
  1677. uint8_t delete_old_data; /* if non zero then data are deleted when disk space is exhausted */
  1678. struct dbengine_chart_thread *chart_threads[]; /* dset_charts elements */
  1679. };
  1680. static void query_dbengine_chart(void *arg)
  1681. {
  1682. struct dbengine_query_thread *thread_info = (struct dbengine_query_thread *)arg;
  1683. const int DSET_CHARTS = thread_info->dset_charts;
  1684. const int DSET_DIMS = thread_info->dset_dims;
  1685. time_t time_after, time_before, time_min, time_approx_min, time_max, duration;
  1686. int i, j, update_every = 1;
  1687. RRDSET *st;
  1688. RRDDIM *rd;
  1689. uint8_t same;
  1690. time_t time_now, time_retrieved;
  1691. collected_number generatedv;
  1692. calculated_number value, expected;
  1693. storage_number n;
  1694. struct rrddim_query_handle handle;
  1695. do {
  1696. // pick a chart and dimension
  1697. i = random() % DSET_CHARTS;
  1698. st = thread_info->chart_threads[i]->st;
  1699. j = random() % DSET_DIMS;
  1700. rd = thread_info->chart_threads[i]->rd[j];
  1701. time_min = thread_info->time_present - thread_info->history_seconds + 1;
  1702. time_max = thread_info->chart_threads[i]->time_max;
  1703. if (thread_info->delete_old_data) {
  1704. /* A time window of twice the disk space is sufficient for compression space savings of up to 50% */
  1705. time_approx_min = time_max - (default_rrdeng_disk_quota_mb * 2 * 1024 * 1024) /
  1706. (((uint64_t) DSET_DIMS * DSET_CHARTS) * sizeof(storage_number));
  1707. time_min = MAX(time_min, time_approx_min);
  1708. }
  1709. if (!time_max) {
  1710. time_before = time_after = time_min;
  1711. } else {
  1712. time_after = time_min + random() % (MAX(time_max - time_min, 1));
  1713. duration = random() % 3600;
  1714. time_before = MIN(time_after + duration, time_max); /* up to 1 hour queries */
  1715. }
  1716. rd->state->query_ops.init(rd, &handle, time_after, time_before);
  1717. ++thread_info->queries_nr;
  1718. for (time_now = time_after ; time_now <= time_before ; time_now += update_every) {
  1719. generatedv = generate_dbengine_chart_value(i, j, time_now);
  1720. expected = unpack_storage_number(pack_storage_number((calculated_number) generatedv, SN_EXISTS));
  1721. if (unlikely(rd->state->query_ops.is_finished(&handle))) {
  1722. if (!thread_info->delete_old_data) { /* data validation only when we don't delete */
  1723. fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, expecting value "
  1724. CALCULATED_NUMBER_FORMAT ", found data gap, ### E R R O R ###\n",
  1725. st->name, rd->name, (unsigned long) time_now, expected);
  1726. ++thread_info->errors;
  1727. }
  1728. break;
  1729. }
  1730. n = rd->state->query_ops.next_metric(&handle, &time_retrieved);
  1731. if (SN_EMPTY_SLOT == n) {
  1732. if (!thread_info->delete_old_data) { /* data validation only when we don't delete */
  1733. fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, expecting value "
  1734. CALCULATED_NUMBER_FORMAT ", found data gap, ### E R R O R ###\n",
  1735. st->name, rd->name, (unsigned long) time_now, expected);
  1736. ++thread_info->errors;
  1737. }
  1738. break;
  1739. }
  1740. ++thread_info->queried_metrics_nr;
  1741. value = unpack_storage_number(n);
  1742. same = (calculated_number_round(value) == calculated_number_round(expected)) ? 1 : 0;
  1743. if (!same) {
  1744. if (!thread_info->delete_old_data) { /* data validation only when we don't delete */
  1745. fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, expecting value "
  1746. CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT
  1747. ", ### E R R O R ###\n",
  1748. st->name, rd->name, (unsigned long) time_now, expected, value);
  1749. ++thread_info->errors;
  1750. }
  1751. }
  1752. if (time_retrieved != time_now) {
  1753. if (!thread_info->delete_old_data) { /* data validation only when we don't delete */
  1754. fprintf(stderr,
  1755. " DB-engine stresstest %s/%s: at %lu secs, found timestamp %lu ### E R R O R ###\n",
  1756. st->name, rd->name, (unsigned long) time_now, (unsigned long) time_retrieved);
  1757. ++thread_info->errors;
  1758. }
  1759. }
  1760. }
  1761. rd->state->query_ops.finalize(&handle);
  1762. } while(!thread_info->done);
  1763. }
  1764. void dbengine_stress_test(unsigned TEST_DURATION_SEC, unsigned DSET_CHARTS, unsigned QUERY_THREADS,
  1765. unsigned RAMP_UP_SECONDS, unsigned PAGE_CACHE_MB, unsigned DISK_SPACE_MB)
  1766. {
  1767. const unsigned DSET_DIMS = 128;
  1768. const uint64_t EXPECTED_COMPRESSION_RATIO = 20;
  1769. const unsigned HISTORY_SECONDS = 3600 * 24 * 365 * 50; /* 50 year of history */
  1770. RRDHOST *host = NULL;
  1771. struct dbengine_chart_thread **chart_threads;
  1772. struct dbengine_query_thread **query_threads;
  1773. unsigned i, j;
  1774. time_t time_start, time_end;
  1775. error_log_limit_unlimited();
  1776. if (!TEST_DURATION_SEC)
  1777. TEST_DURATION_SEC = 10;
  1778. if (!DSET_CHARTS)
  1779. DSET_CHARTS = 1;
  1780. if (!QUERY_THREADS)
  1781. QUERY_THREADS = 1;
  1782. if (PAGE_CACHE_MB < RRDENG_MIN_PAGE_CACHE_SIZE_MB)
  1783. PAGE_CACHE_MB = RRDENG_MIN_PAGE_CACHE_SIZE_MB;
  1784. default_rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE;
  1785. default_rrdeng_page_cache_mb = PAGE_CACHE_MB;
  1786. if (DISK_SPACE_MB) {
  1787. fprintf(stderr, "By setting disk space limit data are allowed to be deleted. "
  1788. "Data validation is turned off for this run.\n");
  1789. default_rrdeng_disk_quota_mb = DISK_SPACE_MB;
  1790. } else {
  1791. // Worst case for uncompressible data
  1792. default_rrdeng_disk_quota_mb =
  1793. (((uint64_t) DSET_DIMS * DSET_CHARTS) * sizeof(storage_number) * HISTORY_SECONDS) / (1024 * 1024);
  1794. default_rrdeng_disk_quota_mb -= default_rrdeng_disk_quota_mb * EXPECTED_COMPRESSION_RATIO / 100;
  1795. }
  1796. fprintf(stderr, "Initializing localhost with hostname 'dbengine-stress-test'\n");
  1797. host = dbengine_rrdhost_find_or_create("dbengine-stress-test");
  1798. if (NULL == host)
  1799. return;
  1800. chart_threads = mallocz(sizeof(*chart_threads) * DSET_CHARTS);
  1801. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1802. chart_threads[i] = mallocz(sizeof(*chart_threads[i]) + sizeof(RRDDIM *) * DSET_DIMS);
  1803. }
  1804. query_threads = mallocz(sizeof(*query_threads) * QUERY_THREADS);
  1805. for (i = 0 ; i < QUERY_THREADS ; ++i) {
  1806. query_threads[i] = mallocz(sizeof(*query_threads[i]) + sizeof(struct dbengine_chart_thread *) * DSET_CHARTS);
  1807. }
  1808. fprintf(stderr, "\nRunning DB-engine stress test, %u seconds writers ramp-up time,\n"
  1809. "%u seconds of concurrent readers and writers, %u writer threads, %u reader threads,\n"
  1810. "%u MiB of page cache.\n",
  1811. RAMP_UP_SECONDS, TEST_DURATION_SEC, DSET_CHARTS, QUERY_THREADS, PAGE_CACHE_MB);
  1812. time_start = now_realtime_sec() + HISTORY_SECONDS; /* move history to the future */
  1813. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1814. chart_threads[i]->host = host;
  1815. chart_threads[i]->chartname = "random";
  1816. chart_threads[i]->dset_charts = DSET_CHARTS;
  1817. chart_threads[i]->chart_i = i;
  1818. chart_threads[i]->dset_dims = DSET_DIMS;
  1819. chart_threads[i]->history_seconds = HISTORY_SECONDS;
  1820. chart_threads[i]->time_present = time_start;
  1821. chart_threads[i]->time_max = 0;
  1822. chart_threads[i]->done = 0;
  1823. chart_threads[i]->errors = chart_threads[i]->stored_metrics_nr = 0;
  1824. init_completion(&chart_threads[i]->charts_initialized);
  1825. assert(0 == uv_thread_create(&chart_threads[i]->thread, generate_dbengine_chart, chart_threads[i]));
  1826. }
  1827. /* barrier so that subsequent queries can access valid chart data */
  1828. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1829. wait_for_completion(&chart_threads[i]->charts_initialized);
  1830. destroy_completion(&chart_threads[i]->charts_initialized);
  1831. }
  1832. sleep(RAMP_UP_SECONDS);
  1833. /* at this point data have already began being written to the database */
  1834. for (i = 0 ; i < QUERY_THREADS ; ++i) {
  1835. query_threads[i]->host = host;
  1836. query_threads[i]->chartname = "random";
  1837. query_threads[i]->dset_charts = DSET_CHARTS;
  1838. query_threads[i]->dset_dims = DSET_DIMS;
  1839. query_threads[i]->history_seconds = HISTORY_SECONDS;
  1840. query_threads[i]->time_present = time_start;
  1841. query_threads[i]->done = 0;
  1842. query_threads[i]->errors = query_threads[i]->queries_nr = query_threads[i]->queried_metrics_nr = 0;
  1843. for (j = 0 ; j < DSET_CHARTS ; ++j) {
  1844. query_threads[i]->chart_threads[j] = chart_threads[j];
  1845. }
  1846. query_threads[i]->delete_old_data = DISK_SPACE_MB ? 1 : 0;
  1847. assert(0 == uv_thread_create(&query_threads[i]->thread, query_dbengine_chart, query_threads[i]));
  1848. }
  1849. sleep(TEST_DURATION_SEC);
  1850. /* stop workload */
  1851. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1852. chart_threads[i]->done = 1;
  1853. }
  1854. for (i = 0 ; i < QUERY_THREADS ; ++i) {
  1855. query_threads[i]->done = 1;
  1856. }
  1857. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1858. assert(0 == uv_thread_join(&chart_threads[i]->thread));
  1859. }
  1860. for (i = 0 ; i < QUERY_THREADS ; ++i) {
  1861. assert(0 == uv_thread_join(&query_threads[i]->thread));
  1862. }
  1863. time_end = now_realtime_sec();
  1864. fprintf(stderr, "\nDB-engine stress test finished in %ld seconds.\n", time_end - time_start);
  1865. unsigned long stored_metrics_nr = 0;
  1866. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1867. stored_metrics_nr += chart_threads[i]->stored_metrics_nr;
  1868. }
  1869. unsigned long queries_nr = 0, queried_metrics_nr = 0;
  1870. for (i = 0 ; i < QUERY_THREADS ; ++i) {
  1871. queries_nr += query_threads[i]->queries_nr;
  1872. queried_metrics_nr += query_threads[i]->queried_metrics_nr;
  1873. }
  1874. fprintf(stderr, "%u metrics were stored (dataset size of %lu MiB) in %u charts by 1 writer thread per chart.\n",
  1875. DSET_CHARTS * DSET_DIMS, stored_metrics_nr * sizeof(storage_number) / (1024 * 1024), DSET_CHARTS);
  1876. fprintf(stderr, "Metrics were being generated per 1 emulated second and time was accelerated.\n");
  1877. fprintf(stderr, "%lu metric data points were queried by %u reader threads.\n", queried_metrics_nr, QUERY_THREADS);
  1878. fprintf(stderr, "Query starting time is randomly chosen from the beginning of the time-series up to the time of\n"
  1879. "the latest data point, and ending time from 1 second up to 1 hour after the starting time.\n");
  1880. fprintf(stderr, "Performance is %lu written data points/sec and %lu read data points/sec.\n",
  1881. stored_metrics_nr / (time_end - time_start), queried_metrics_nr / (time_end - time_start));
  1882. for (i = 0 ; i < DSET_CHARTS ; ++i) {
  1883. freez(chart_threads[i]);
  1884. }
  1885. freez(chart_threads);
  1886. for (i = 0 ; i < QUERY_THREADS ; ++i) {
  1887. freez(query_threads[i]);
  1888. }
  1889. freez(query_threads);
  1890. rrd_wrlock();
  1891. rrdeng_prepare_exit(host->rrdeng_ctx);
  1892. rrdhost_delete_charts(host);
  1893. rrdeng_exit(host->rrdeng_ctx);
  1894. rrd_unlock();
  1895. }
  1896. #endif