progress.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "progress.h"
  3. #define PROGRESS_CACHE_SIZE 200
  4. // ----------------------------------------------------------------------------
  5. // hashtable for HASHED_KEY
  6. // cleanup hashtable defines
  7. #include "../simple_hashtable_undef.h"
  8. struct query;
  9. #define SIMPLE_HASHTABLE_VALUE_TYPE struct query
  10. #define SIMPLE_HASHTABLE_KEY_TYPE uuid_t
  11. #define SIMPLE_HASHTABLE_NAME _QUERY
  12. #define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION query_transaction
  13. #define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION query_compare_keys
  14. #include "../simple_hashtable.h"
  15. // ----------------------------------------------------------------------------
  16. typedef struct query {
  17. uuid_t transaction;
  18. BUFFER *query;
  19. BUFFER *payload;
  20. BUFFER *client;
  21. usec_t started_ut;
  22. usec_t finished_ut;
  23. HTTP_REQUEST_MODE mode;
  24. HTTP_ACL acl;
  25. uint32_t sent_size;
  26. uint32_t response_size;
  27. short response_code;
  28. bool indexed;
  29. uint32_t updates;
  30. usec_t duration_ut;
  31. size_t all;
  32. size_t done;
  33. struct query *prev, *next;
  34. } QUERY_PROGRESS;
  35. static inline uuid_t *query_transaction(QUERY_PROGRESS *qp) {
  36. return qp ? &qp->transaction : NULL;
  37. }
  38. static inline bool query_compare_keys(uuid_t *t1, uuid_t *t2) {
  39. if(t1 == t2 || (t1 && t2 && memcmp(t1, t2, sizeof(uuid_t)) == 0))
  40. return true;
  41. return false;
  42. }
  43. static struct progress {
  44. SPINLOCK spinlock;
  45. bool initialized;
  46. struct {
  47. size_t available;
  48. QUERY_PROGRESS *list;
  49. } cache;
  50. SIMPLE_HASHTABLE_QUERY hashtable;
  51. } progress = {
  52. .initialized = false,
  53. };
  54. SIMPLE_HASHTABLE_HASH query_hash(uuid_t *transaction) {
  55. struct uuid_hi_lo_t {
  56. uint64_t hi;
  57. uint64_t lo;
  58. } *parts = (struct uuid_hi_lo_t *)transaction;
  59. return parts->lo;
  60. }
  61. static void query_progress_init_unsafe(void) {
  62. if(!progress.initialized) {
  63. memset(&progress, 0, sizeof(progress));
  64. simple_hashtable_init_QUERY(&progress.hashtable, PROGRESS_CACHE_SIZE * 4);
  65. progress.initialized = true;
  66. }
  67. }
  68. // ----------------------------------------------------------------------------
  69. static inline QUERY_PROGRESS *query_progress_find_in_hashtable_unsafe(uuid_t *transaction) {
  70. SIMPLE_HASHTABLE_HASH hash = query_hash(transaction);
  71. SIMPLE_HASHTABLE_SLOT_QUERY *slot = simple_hashtable_get_slot_QUERY(&progress.hashtable, hash, transaction, true);
  72. QUERY_PROGRESS *qp = SIMPLE_HASHTABLE_SLOT_DATA(slot);
  73. assert(!qp || qp->indexed);
  74. return qp;
  75. }
  76. static inline void query_progress_add_to_hashtable_unsafe(QUERY_PROGRESS *qp) {
  77. assert(!qp->indexed);
  78. SIMPLE_HASHTABLE_HASH hash = query_hash(&qp->transaction);
  79. SIMPLE_HASHTABLE_SLOT_QUERY *slot =
  80. simple_hashtable_get_slot_QUERY(&progress.hashtable, hash, &qp->transaction, true);
  81. internal_fatal(SIMPLE_HASHTABLE_SLOT_DATA(slot) != NULL && SIMPLE_HASHTABLE_SLOT_DATA(slot) != qp,
  82. "Attempt to overwrite a progress slot, with another value");
  83. simple_hashtable_set_slot_QUERY(&progress.hashtable, slot, hash, qp);
  84. qp->indexed = true;
  85. }
  86. static inline void query_progress_remove_from_hashtable_unsafe(QUERY_PROGRESS *qp) {
  87. assert(qp->indexed);
  88. SIMPLE_HASHTABLE_HASH hash = query_hash(&qp->transaction);
  89. SIMPLE_HASHTABLE_SLOT_QUERY *slot =
  90. simple_hashtable_get_slot_QUERY(&progress.hashtable, hash, &qp->transaction, true);
  91. if(SIMPLE_HASHTABLE_SLOT_DATA(slot) == qp)
  92. simple_hashtable_del_slot_QUERY(&progress.hashtable, slot);
  93. else
  94. internal_fatal(SIMPLE_HASHTABLE_SLOT_DATA(slot) != NULL,
  95. "Attempt to remove from the hashtable a progress slot with a different value");
  96. qp->indexed = false;
  97. }
  98. // ----------------------------------------------------------------------------
  99. static QUERY_PROGRESS *query_progress_alloc(uuid_t *transaction) {
  100. QUERY_PROGRESS *qp;
  101. qp = callocz(1, sizeof(*qp));
  102. uuid_copy(qp->transaction, *transaction);
  103. qp->query = buffer_create(0, NULL);
  104. qp->payload = buffer_create(0, NULL);
  105. qp->client = buffer_create(0, NULL);
  106. return qp;
  107. }
  108. static void query_progress_free(QUERY_PROGRESS *qp) {
  109. if(!qp) return;
  110. buffer_free(qp->query);
  111. buffer_free(qp->payload);
  112. buffer_free(qp->client);
  113. freez(qp);
  114. }
  115. static void query_progress_cleanup_to_reuse(QUERY_PROGRESS *qp, uuid_t *transaction) {
  116. assert(qp && qp->prev == NULL && qp->next == NULL);
  117. assert(!transaction || !qp->indexed);
  118. buffer_flush(qp->query);
  119. buffer_flush(qp->payload);
  120. buffer_flush(qp->client);
  121. qp->started_ut = qp->finished_ut = qp->duration_ut = 0;
  122. qp->all = qp->done = qp->updates = 0;
  123. qp->acl = 0;
  124. qp->next = qp->prev = NULL;
  125. qp->response_size = qp->sent_size = 0;
  126. qp->response_code = 0;
  127. if(transaction)
  128. uuid_copy(qp->transaction, *transaction);
  129. }
  130. static inline void query_progress_update(QUERY_PROGRESS *qp, usec_t started_ut, HTTP_REQUEST_MODE mode, HTTP_ACL acl, const char *query, BUFFER *payload, const char *client) {
  131. qp->mode = mode;
  132. qp->acl = acl;
  133. qp->started_ut = started_ut ? started_ut : now_realtime_usec();
  134. qp->finished_ut = 0;
  135. qp->duration_ut = 0;
  136. qp->response_size = 0;
  137. qp->sent_size = 0;
  138. qp->response_code = 0;
  139. if(query && *query && !buffer_strlen(qp->query))
  140. buffer_strcat(qp->query, query);
  141. if(payload && !buffer_strlen(qp->payload))
  142. buffer_copy(qp->payload, payload);
  143. if(client && *client && !buffer_strlen(qp->client))
  144. buffer_strcat(qp->client, client);
  145. }
  146. // ----------------------------------------------------------------------------
  147. static inline void query_progress_link_to_cache_unsafe(QUERY_PROGRESS *qp) {
  148. assert(!qp->prev && !qp->next);
  149. DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(progress.cache.list, qp, prev, next);
  150. progress.cache.available++;
  151. }
  152. static inline void query_progress_unlink_from_cache_unsafe(QUERY_PROGRESS *qp) {
  153. assert(qp->prev);
  154. DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(progress.cache.list, qp, prev, next);
  155. progress.cache.available--;
  156. }
  157. // ----------------------------------------------------------------------------
  158. // Progress API
  159. void query_progress_start_or_update(uuid_t *transaction, usec_t started_ut, HTTP_REQUEST_MODE mode, HTTP_ACL acl, const char *query, BUFFER *payload, const char *client) {
  160. if(!transaction)
  161. return;
  162. spinlock_lock(&progress.spinlock);
  163. query_progress_init_unsafe();
  164. QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
  165. if(qp) {
  166. // the transaction is already there
  167. if(qp->prev) {
  168. // reusing a finished transaction
  169. query_progress_unlink_from_cache_unsafe(qp);
  170. query_progress_cleanup_to_reuse(qp, NULL);
  171. }
  172. }
  173. else if (progress.cache.available >= PROGRESS_CACHE_SIZE && progress.cache.list) {
  174. // transaction is not found - get the first available, if any.
  175. qp = progress.cache.list;
  176. query_progress_unlink_from_cache_unsafe(qp);
  177. query_progress_remove_from_hashtable_unsafe(qp);
  178. query_progress_cleanup_to_reuse(qp, transaction);
  179. }
  180. else {
  181. qp = query_progress_alloc(transaction);
  182. }
  183. query_progress_update(qp, started_ut, mode, acl, query, payload, client);
  184. if(!qp->indexed)
  185. query_progress_add_to_hashtable_unsafe(qp);
  186. spinlock_unlock(&progress.spinlock);
  187. }
  188. void query_progress_set_finish_line(uuid_t *transaction, size_t all) {
  189. if(!transaction)
  190. return;
  191. spinlock_lock(&progress.spinlock);
  192. query_progress_init_unsafe();
  193. QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
  194. if(qp) {
  195. qp->updates++;
  196. if(all > qp->all)
  197. qp->all = all;
  198. }
  199. spinlock_unlock(&progress.spinlock);
  200. }
  201. void query_progress_done_step(uuid_t *transaction, size_t done) {
  202. if(!transaction)
  203. return;
  204. spinlock_lock(&progress.spinlock);
  205. query_progress_init_unsafe();
  206. QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
  207. if(qp) {
  208. qp->updates++;
  209. qp->done += done;
  210. }
  211. spinlock_unlock(&progress.spinlock);
  212. }
  213. void query_progress_finished(uuid_t *transaction, usec_t finished_ut, short int response_code, usec_t duration_ut, size_t response_size, size_t sent_size) {
  214. if(!transaction)
  215. return;
  216. spinlock_lock(&progress.spinlock);
  217. query_progress_init_unsafe();
  218. // find this transaction to update it
  219. {
  220. QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
  221. if(qp) {
  222. qp->sent_size = sent_size;
  223. qp->response_size = response_size;
  224. qp->response_code = response_code;
  225. qp->duration_ut = duration_ut;
  226. qp->finished_ut = finished_ut ? finished_ut : now_realtime_usec();
  227. if(qp->prev)
  228. query_progress_unlink_from_cache_unsafe(qp);
  229. query_progress_link_to_cache_unsafe(qp);
  230. }
  231. }
  232. // find an item to free
  233. {
  234. QUERY_PROGRESS *qp_to_free = NULL;
  235. if(progress.cache.available > PROGRESS_CACHE_SIZE && progress.cache.list) {
  236. qp_to_free = progress.cache.list;
  237. query_progress_unlink_from_cache_unsafe(qp_to_free);
  238. query_progress_remove_from_hashtable_unsafe(qp_to_free);
  239. }
  240. spinlock_unlock(&progress.spinlock);
  241. query_progress_free(qp_to_free);
  242. }
  243. }
  244. void query_progress_functions_update(uuid_t *transaction, size_t done, size_t all) {
  245. // functions send to the total 'done', not the increment
  246. if(!transaction)
  247. return;
  248. spinlock_lock(&progress.spinlock);
  249. query_progress_init_unsafe();
  250. QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
  251. if(qp) {
  252. if(all)
  253. qp->all = all;
  254. if(done)
  255. qp->done = done;
  256. qp->updates++;
  257. }
  258. spinlock_unlock(&progress.spinlock);
  259. }
  260. // ----------------------------------------------------------------------------
  261. // /api/v2/progress - to get the progress of a transaction
  262. int web_api_v2_report_progress(uuid_t *transaction, BUFFER *wb) {
  263. buffer_flush(wb);
  264. buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
  265. if(!transaction) {
  266. buffer_json_member_add_uint64(wb, "status", 400);
  267. buffer_json_member_add_string(wb, "message", "No transaction given");
  268. buffer_json_finalize(wb);
  269. return 400;
  270. }
  271. spinlock_lock(&progress.spinlock);
  272. query_progress_init_unsafe();
  273. QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
  274. if(!qp) {
  275. spinlock_unlock(&progress.spinlock);
  276. buffer_json_member_add_uint64(wb, "status", HTTP_RESP_NOT_FOUND);
  277. buffer_json_member_add_string(wb, "message", "Transaction not found");
  278. buffer_json_finalize(wb);
  279. return HTTP_RESP_NOT_FOUND;
  280. }
  281. buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
  282. buffer_json_member_add_uint64(wb, "started_ut", qp->started_ut);
  283. if(qp->finished_ut) {
  284. buffer_json_member_add_uint64(wb, "finished_ut", qp->finished_ut);
  285. buffer_json_member_add_double(wb, "progress", 100.0);
  286. buffer_json_member_add_uint64(wb, "age_ut", qp->finished_ut - qp->started_ut);
  287. }
  288. else {
  289. usec_t now_ut = now_realtime_usec();
  290. buffer_json_member_add_uint64(wb, "now_ut", now_ut);
  291. buffer_json_member_add_uint64(wb, "age_ut", now_ut - qp->started_ut);
  292. if (qp->all)
  293. buffer_json_member_add_double(wb, "progress", (double) qp->done * 100.0 / (double) qp->all);
  294. else
  295. buffer_json_member_add_uint64(wb, "working", qp->done);
  296. }
  297. buffer_json_finalize(wb);
  298. spinlock_unlock(&progress.spinlock);
  299. return 200;
  300. }
  301. // ----------------------------------------------------------------------------
  302. // function to show the progress of all current queries
  303. // and the recent few completed queries
  304. int progress_function_result(BUFFER *wb, const char *hostname) {
  305. buffer_flush(wb);
  306. wb->content_type = CT_APPLICATION_JSON;
  307. buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
  308. buffer_json_member_add_string(wb, "hostname", hostname);
  309. buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
  310. buffer_json_member_add_string(wb, "type", "table");
  311. buffer_json_member_add_time_t(wb, "update_every", 1);
  312. buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_PROGRESS_HELP);
  313. buffer_json_member_add_array(wb, "data");
  314. spinlock_lock(&progress.spinlock);
  315. query_progress_init_unsafe();
  316. usec_t now_ut = now_realtime_usec();
  317. usec_t max_duration_ut = 0;
  318. size_t max_size = 0, max_sent = 0;
  319. size_t archived = 0, running = 0;
  320. SIMPLE_HASHTABLE_FOREACH_READ_ONLY(&progress.hashtable, sl, _QUERY) {
  321. QUERY_PROGRESS *qp = SIMPLE_HASHTABLE_FOREACH_READ_ONLY_VALUE(sl);
  322. if(unlikely(!qp)) continue; // not really needed, just for completeness
  323. if(qp->prev)
  324. archived++;
  325. else
  326. running++;
  327. bool finished = qp->finished_ut ? true : false;
  328. usec_t duration_ut = finished ? qp->duration_ut : now_ut - qp->started_ut;
  329. if(duration_ut > max_duration_ut)
  330. max_duration_ut = duration_ut;
  331. if(finished) {
  332. if(qp->response_size > max_size)
  333. max_size = qp->response_size;
  334. if(qp->sent_size > max_sent)
  335. max_sent = qp->sent_size;
  336. }
  337. buffer_json_add_array_item_array(wb); // row
  338. buffer_json_add_array_item_uuid_compact(wb, &qp->transaction);
  339. buffer_json_add_array_item_uint64(wb, qp->started_ut);
  340. buffer_json_add_array_item_string(wb, http_request_method2string(qp->mode));
  341. buffer_json_add_array_item_string(wb, buffer_tostring(qp->query));
  342. if(!buffer_strlen(qp->client)) {
  343. if(qp->acl & HTTP_ACL_ACLK)
  344. buffer_json_add_array_item_string(wb, "ACLK");
  345. else if(qp->acl & HTTP_ACL_WEBRTC)
  346. buffer_json_add_array_item_string(wb, "WEBRTC");
  347. else
  348. buffer_json_add_array_item_string(wb, "unknown");
  349. }
  350. else
  351. buffer_json_add_array_item_string(wb, buffer_tostring(qp->client));
  352. if(finished) {
  353. buffer_json_add_array_item_string(wb, "finished");
  354. buffer_json_add_array_item_string(wb, "100.00 %%");
  355. }
  356. else {
  357. char buf[50];
  358. buffer_json_add_array_item_string(wb, "in-progress");
  359. if (qp->all)
  360. snprintfz(buf, sizeof(buf), "%0.2f %%", (double) qp->done * 100.0 / (double) qp->all);
  361. else
  362. snprintfz(buf, sizeof(buf), "%zu", qp->done);
  363. buffer_json_add_array_item_string(wb, buf);
  364. }
  365. buffer_json_add_array_item_double(wb, (double)duration_ut / USEC_PER_MS);
  366. if(finished) {
  367. buffer_json_add_array_item_uint64(wb, qp->response_code);
  368. buffer_json_add_array_item_uint64(wb, qp->response_size);
  369. buffer_json_add_array_item_uint64(wb, qp->sent_size);
  370. }
  371. else {
  372. buffer_json_add_array_item_string(wb, NULL);
  373. buffer_json_add_array_item_string(wb, NULL);
  374. buffer_json_add_array_item_string(wb, NULL);
  375. }
  376. buffer_json_add_array_item_object(wb); // row options
  377. {
  378. char *severity = "notice";
  379. if(finished) {
  380. if(qp->response_code == HTTP_RESP_NOT_MODIFIED ||
  381. qp->response_code == HTTP_RESP_CLIENT_CLOSED_REQUEST ||
  382. qp->response_code == HTTP_RESP_CONFLICT)
  383. severity = "debug";
  384. else if(qp->response_code >= 500 && qp->response_code <= 599)
  385. severity = "error";
  386. else if(qp->response_code >= 400 && qp->response_code <= 499)
  387. severity = "warning";
  388. else if(qp->response_code >= 300 && qp->response_code <= 399)
  389. severity = "notice";
  390. else
  391. severity = "normal";
  392. }
  393. buffer_json_member_add_string(wb, "severity", severity);
  394. }
  395. buffer_json_object_close(wb); // row options
  396. buffer_json_array_close(wb); // row
  397. }
  398. assert(archived == progress.cache.available);
  399. spinlock_unlock(&progress.spinlock);
  400. buffer_json_array_close(wb); // data
  401. buffer_json_member_add_object(wb, "columns");
  402. {
  403. size_t field_id = 0;
  404. // transaction
  405. buffer_rrdf_table_add_field(wb, field_id++, "Transaction", "Transaction ID",
  406. RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  407. 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
  408. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
  409. RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
  410. NULL);
  411. // timestamp
  412. buffer_rrdf_table_add_field(wb, field_id++, "Started", "Query Start Timestamp",
  413. RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_USEC,
  414. 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
  415. RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_NONE,
  416. RRDF_FIELD_OPTS_VISIBLE, NULL);
  417. // request method
  418. buffer_rrdf_table_add_field(wb, field_id++, "Method", "Request Method",
  419. RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  420. 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
  421. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
  422. RRDF_FIELD_OPTS_VISIBLE, NULL);
  423. // query
  424. buffer_rrdf_table_add_field(wb, field_id++, "Query", "Query",
  425. RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  426. 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
  427. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
  428. RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_FULL_WIDTH | RRDF_FIELD_OPTS_WRAP, NULL);
  429. // client
  430. buffer_rrdf_table_add_field(wb, field_id++, "Client", "Client",
  431. RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  432. 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
  433. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
  434. RRDF_FIELD_OPTS_VISIBLE, NULL);
  435. // status
  436. buffer_rrdf_table_add_field(wb, field_id++, "Status", "Query Status",
  437. RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  438. 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
  439. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
  440. RRDF_FIELD_OPTS_VISIBLE, NULL);
  441. // progress
  442. buffer_rrdf_table_add_field(wb, field_id++, "Progress", "Query Progress",
  443. RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  444. 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
  445. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
  446. RRDF_FIELD_OPTS_VISIBLE, NULL);
  447. // duration
  448. buffer_rrdf_table_add_field(wb, field_id++, "Duration", "Query Duration",
  449. RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
  450. 2, "ms", (double)max_duration_ut / USEC_PER_MS, RRDF_FIELD_SORT_DESCENDING, NULL,
  451. RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
  452. RRDF_FIELD_OPTS_VISIBLE, NULL);
  453. // response code
  454. buffer_rrdf_table_add_field(wb, field_id++, "Response", "Query Response Code",
  455. RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  456. 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
  457. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
  458. RRDF_FIELD_OPTS_VISIBLE, NULL);
  459. // response size
  460. buffer_rrdf_table_add_field(wb, field_id++, "Size", "Query Response Size",
  461. RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  462. 0, "bytes", (double)max_size, RRDF_FIELD_SORT_DESCENDING, NULL,
  463. RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
  464. RRDF_FIELD_OPTS_NONE, NULL);
  465. // sent size
  466. buffer_rrdf_table_add_field(wb, field_id++, "Sent", "Query Response Final Size",
  467. RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
  468. 0, "bytes", (double)max_sent, RRDF_FIELD_SORT_DESCENDING, NULL,
  469. RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
  470. RRDF_FIELD_OPTS_NONE, NULL);
  471. // row options
  472. buffer_rrdf_table_add_field(wb, field_id++, "rowOptions", "rowOptions",
  473. RRDF_FIELD_TYPE_NONE, RRDR_FIELD_VISUAL_ROW_OPTIONS, RRDF_FIELD_TRANSFORM_NONE,
  474. 0, NULL, NAN, RRDF_FIELD_SORT_FIXED, NULL,
  475. RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
  476. RRDF_FIELD_OPTS_DUMMY, NULL);
  477. }
  478. buffer_json_object_close(wb); // columns
  479. buffer_json_member_add_string(wb, "default_sort_column", "Started");
  480. buffer_json_member_add_time_t(wb, "expires", (time_t)((now_ut / USEC_PER_SEC) + 1));
  481. buffer_json_finalize(wb);
  482. return 200;
  483. }
  484. // ----------------------------------------------------------------------------
  485. int progress_unittest(void) {
  486. size_t permanent = 100;
  487. uuid_t valid[permanent];
  488. usec_t started = now_monotonic_usec();
  489. for(size_t i = 0; i < permanent ;i++) {
  490. uuid_generate_random(valid[i]);
  491. query_progress_start_or_update(&valid[i], 0, HTTP_REQUEST_MODE_GET, HTTP_ACL_ACLK, "permanent", NULL, "test");
  492. }
  493. for(size_t n = 0; n < 5000000 ;n++) {
  494. uuid_t t;
  495. uuid_generate_random(t);
  496. query_progress_start_or_update(&t, 0, HTTP_REQUEST_MODE_OPTIONS, HTTP_ACL_WEBRTC, "ephemeral", NULL, "test");
  497. query_progress_finished(&t, 0, 200, 1234, 123, 12);
  498. QUERY_PROGRESS *qp;
  499. for(size_t i = 0; i < permanent ;i++) {
  500. qp = query_progress_find_in_hashtable_unsafe(&valid[i]);
  501. assert(qp);
  502. (void)qp;
  503. }
  504. }
  505. usec_t ended = now_monotonic_usec();
  506. usec_t duration = ended - started;
  507. printf("progress hashtable resizes: %zu, size: %zu, used: %zu, deleted: %zu, searches: %zu, collisions: %zu, additions: %zu, deletions: %zu\n",
  508. progress.hashtable.resizes,
  509. progress.hashtable.size, progress.hashtable.used, progress.hashtable.deleted,
  510. progress.hashtable.searches, progress.hashtable.collisions, progress.hashtable.additions, progress.hashtable.deletions);
  511. double d = (double)duration / USEC_PER_SEC;
  512. printf("hashtable ops: %0.2f / sec\n", (double)progress.hashtable.searches / d);
  513. return 0;
  514. }