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