mirror of
https://github.com/netdata/netdata.git
synced 2025-04-26 22:04:46 +00:00

* split rrdfunctions streaming and progress * simplified internal inline functions API * split rrdfunctions inflight management * split rrd functions exporters * renames * base dyncfg structure * config pluginsd * intercept dyncfg function calls * loading and saving of dyncfg metadata and data * save metadata and payload to a single file; added code to update the plugins with jobs and saved configs * basic working unit test * added payload to functions execution * removed old dyncfg code that is not needed any more * more cleanup * cleanup sender for functions with payload * dyncfg functions are not exposed as functions * remaining work to avoid indexing the \0 terminating character in dictionary keys * added back old dyncfg plugins.d commands as noop, to allow plugins continue working * working api; working streaming; * updated plugins.d documentation * aclk and http api requests share the same header parsing logic * added source type internal * fixed crashes * added god mode for tests * fixes * fixed messages * save host machine guids to configs * cleaner manipulation of supported commands * the functions event loop for external plugins can now process dyncfg requests * unified internal and external plugins dyncfg API * Netdata serves schema requests from /etc/netdata/schema.d and /var/lib/netdata/conf.d/schema.d * cleanup and various fixes; fixed bug in previous dyncfg implementation on streaming that was sending the paylod in a way that allowed other streaming commands to be multiplexed * internals go to a separate header file * fix duplicate ACLK requests sent by aclk queue mechanism * use fstat instead of stat * working api * plugin actions renamed to create and delete; dyncfg files are removed only from user actions * prevent deadlock by using the react callback * fix for string_strndupz() * better dyncfg unittests * more tests at the unittests * properly detect dyncfg functions * hide config functions from the UI * tree response improvements * send the initial update with payload * determine tty using stdout, not stderr * changes to statuses, cleanup and the code to bring all business logic into interception * do not crash when the status is empty * functions now propagate the source of the requests to plugins * avoid warning about unused functions * in the count at items for attention, do not count the orphan entries * save source into dyncfg * make the list null terminated * fixed invalid comparison * prevent memory leak on duplicated headers; log x-forwarded-for * more unit tests * added dyncfg unittests into the default unittests * more unit tests and fixes * more unit tests and fixes * fix dictionary unittests * config functions require admin access
655 lines
23 KiB
C
655 lines
23 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "progress.h"
|
|
|
|
#define PROGRESS_CACHE_SIZE 200
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// hashtable for HASHED_KEY
|
|
|
|
// cleanup hashtable defines
|
|
#include "../simple_hashtable_undef.h"
|
|
|
|
struct query;
|
|
#define SIMPLE_HASHTABLE_VALUE_TYPE struct query
|
|
#define SIMPLE_HASHTABLE_KEY_TYPE uuid_t
|
|
#define SIMPLE_HASHTABLE_NAME _QUERY
|
|
#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION query_transaction
|
|
#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION query_compare_keys
|
|
#include "../simple_hashtable.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
typedef struct query {
|
|
uuid_t transaction;
|
|
|
|
BUFFER *query;
|
|
BUFFER *payload;
|
|
BUFFER *client;
|
|
|
|
usec_t started_ut;
|
|
usec_t finished_ut;
|
|
|
|
HTTP_REQUEST_MODE mode;
|
|
HTTP_ACL acl;
|
|
|
|
uint32_t sent_size;
|
|
uint32_t response_size;
|
|
short response_code;
|
|
|
|
bool indexed;
|
|
|
|
uint32_t updates;
|
|
|
|
usec_t duration_ut;
|
|
size_t all;
|
|
size_t done;
|
|
|
|
struct query *prev, *next;
|
|
} QUERY_PROGRESS;
|
|
|
|
static inline uuid_t *query_transaction(QUERY_PROGRESS *qp) {
|
|
return qp ? &qp->transaction : NULL;
|
|
}
|
|
|
|
static inline bool query_compare_keys(uuid_t *t1, uuid_t *t2) {
|
|
if(t1 == t2 || (t1 && t2 && memcmp(t1, t2, sizeof(uuid_t)) == 0))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static struct progress {
|
|
SPINLOCK spinlock;
|
|
bool initialized;
|
|
|
|
struct {
|
|
size_t available;
|
|
QUERY_PROGRESS *list;
|
|
} cache;
|
|
|
|
SIMPLE_HASHTABLE_QUERY hashtable;
|
|
|
|
} progress = {
|
|
.initialized = false,
|
|
};
|
|
|
|
SIMPLE_HASHTABLE_HASH query_hash(uuid_t *transaction) {
|
|
struct uuid_hi_lo_t {
|
|
uint64_t hi;
|
|
uint64_t lo;
|
|
} *parts = (struct uuid_hi_lo_t *)transaction;
|
|
|
|
return parts->lo;
|
|
}
|
|
|
|
static void query_progress_init_unsafe(void) {
|
|
if(!progress.initialized) {
|
|
memset(&progress, 0, sizeof(progress));
|
|
simple_hashtable_init_QUERY(&progress.hashtable, PROGRESS_CACHE_SIZE * 4);
|
|
progress.initialized = true;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static inline QUERY_PROGRESS *query_progress_find_in_hashtable_unsafe(uuid_t *transaction) {
|
|
SIMPLE_HASHTABLE_HASH hash = query_hash(transaction);
|
|
SIMPLE_HASHTABLE_SLOT_QUERY *slot = simple_hashtable_get_slot_QUERY(&progress.hashtable, hash, transaction, true);
|
|
QUERY_PROGRESS *qp = SIMPLE_HASHTABLE_SLOT_DATA(slot);
|
|
|
|
assert(!qp || qp->indexed);
|
|
|
|
return qp;
|
|
}
|
|
|
|
static inline void query_progress_add_to_hashtable_unsafe(QUERY_PROGRESS *qp) {
|
|
assert(!qp->indexed);
|
|
|
|
SIMPLE_HASHTABLE_HASH hash = query_hash(&qp->transaction);
|
|
SIMPLE_HASHTABLE_SLOT_QUERY *slot =
|
|
simple_hashtable_get_slot_QUERY(&progress.hashtable, hash, &qp->transaction, true);
|
|
|
|
internal_fatal(SIMPLE_HASHTABLE_SLOT_DATA(slot) != NULL && SIMPLE_HASHTABLE_SLOT_DATA(slot) != qp,
|
|
"Attempt to overwrite a progress slot, with another value");
|
|
|
|
simple_hashtable_set_slot_QUERY(&progress.hashtable, slot, hash, qp);
|
|
|
|
qp->indexed = true;
|
|
}
|
|
|
|
static inline void query_progress_remove_from_hashtable_unsafe(QUERY_PROGRESS *qp) {
|
|
assert(qp->indexed);
|
|
|
|
SIMPLE_HASHTABLE_HASH hash = query_hash(&qp->transaction);
|
|
SIMPLE_HASHTABLE_SLOT_QUERY *slot =
|
|
simple_hashtable_get_slot_QUERY(&progress.hashtable, hash, &qp->transaction, true);
|
|
|
|
if(SIMPLE_HASHTABLE_SLOT_DATA(slot) == qp)
|
|
simple_hashtable_del_slot_QUERY(&progress.hashtable, slot);
|
|
else
|
|
internal_fatal(SIMPLE_HASHTABLE_SLOT_DATA(slot) != NULL,
|
|
"Attempt to remove from the hashtable a progress slot with a different value");
|
|
|
|
qp->indexed = false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static QUERY_PROGRESS *query_progress_alloc(uuid_t *transaction) {
|
|
QUERY_PROGRESS *qp;
|
|
qp = callocz(1, sizeof(*qp));
|
|
uuid_copy(qp->transaction, *transaction);
|
|
qp->query = buffer_create(0, NULL);
|
|
qp->payload = buffer_create(0, NULL);
|
|
qp->client = buffer_create(0, NULL);
|
|
return qp;
|
|
}
|
|
|
|
static void query_progress_free(QUERY_PROGRESS *qp) {
|
|
if(!qp) return;
|
|
|
|
buffer_free(qp->query);
|
|
buffer_free(qp->payload);
|
|
buffer_free(qp->client);
|
|
freez(qp);
|
|
}
|
|
|
|
static void query_progress_cleanup_to_reuse(QUERY_PROGRESS *qp, uuid_t *transaction) {
|
|
assert(qp && qp->prev == NULL && qp->next == NULL);
|
|
assert(!transaction || !qp->indexed);
|
|
|
|
buffer_flush(qp->query);
|
|
buffer_flush(qp->payload);
|
|
buffer_flush(qp->client);
|
|
qp->started_ut = qp->finished_ut = qp->duration_ut = 0;
|
|
qp->all = qp->done = qp->updates = 0;
|
|
qp->acl = 0;
|
|
qp->next = qp->prev = NULL;
|
|
qp->response_size = qp->sent_size = 0;
|
|
qp->response_code = 0;
|
|
|
|
if(transaction)
|
|
uuid_copy(qp->transaction, *transaction);
|
|
}
|
|
|
|
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) {
|
|
qp->mode = mode;
|
|
qp->acl = acl;
|
|
qp->started_ut = started_ut ? started_ut : now_realtime_usec();
|
|
qp->finished_ut = 0;
|
|
qp->duration_ut = 0;
|
|
qp->response_size = 0;
|
|
qp->sent_size = 0;
|
|
qp->response_code = 0;
|
|
|
|
if(query && *query && !buffer_strlen(qp->query))
|
|
buffer_strcat(qp->query, query);
|
|
|
|
if(payload && !buffer_strlen(qp->payload))
|
|
buffer_copy(qp->payload, payload);
|
|
|
|
if(client && *client && !buffer_strlen(qp->client))
|
|
buffer_strcat(qp->client, client);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static inline void query_progress_link_to_cache_unsafe(QUERY_PROGRESS *qp) {
|
|
assert(!qp->prev && !qp->next);
|
|
DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(progress.cache.list, qp, prev, next);
|
|
progress.cache.available++;
|
|
}
|
|
|
|
static inline void query_progress_unlink_from_cache_unsafe(QUERY_PROGRESS *qp) {
|
|
assert(qp->prev);
|
|
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(progress.cache.list, qp, prev, next);
|
|
progress.cache.available--;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Progress API
|
|
|
|
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) {
|
|
if(!transaction)
|
|
return;
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
|
|
if(qp) {
|
|
// the transaction is already there
|
|
if(qp->prev) {
|
|
// reusing a finished transaction
|
|
query_progress_unlink_from_cache_unsafe(qp);
|
|
query_progress_cleanup_to_reuse(qp, NULL);
|
|
}
|
|
}
|
|
else if (progress.cache.available >= PROGRESS_CACHE_SIZE && progress.cache.list) {
|
|
// transaction is not found - get the first available, if any.
|
|
qp = progress.cache.list;
|
|
query_progress_unlink_from_cache_unsafe(qp);
|
|
|
|
query_progress_remove_from_hashtable_unsafe(qp);
|
|
query_progress_cleanup_to_reuse(qp, transaction);
|
|
}
|
|
else {
|
|
qp = query_progress_alloc(transaction);
|
|
}
|
|
|
|
query_progress_update(qp, started_ut, mode, acl, query, payload, client);
|
|
|
|
if(!qp->indexed)
|
|
query_progress_add_to_hashtable_unsafe(qp);
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
}
|
|
|
|
void query_progress_set_finish_line(uuid_t *transaction, size_t all) {
|
|
if(!transaction)
|
|
return;
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
|
|
if(qp) {
|
|
qp->updates++;
|
|
|
|
if(all > qp->all)
|
|
qp->all = all;
|
|
}
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
}
|
|
|
|
void query_progress_done_step(uuid_t *transaction, size_t done) {
|
|
if(!transaction)
|
|
return;
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
|
|
if(qp) {
|
|
qp->updates++;
|
|
qp->done += done;
|
|
}
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
}
|
|
|
|
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) {
|
|
if(!transaction)
|
|
return;
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
// find this transaction to update it
|
|
{
|
|
QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
|
|
if(qp) {
|
|
qp->sent_size = sent_size;
|
|
qp->response_size = response_size;
|
|
qp->response_code = response_code;
|
|
qp->duration_ut = duration_ut;
|
|
qp->finished_ut = finished_ut ? finished_ut : now_realtime_usec();
|
|
|
|
if(qp->prev)
|
|
query_progress_unlink_from_cache_unsafe(qp);
|
|
|
|
query_progress_link_to_cache_unsafe(qp);
|
|
}
|
|
}
|
|
|
|
// find an item to free
|
|
{
|
|
QUERY_PROGRESS *qp_to_free = NULL;
|
|
if(progress.cache.available > PROGRESS_CACHE_SIZE && progress.cache.list) {
|
|
qp_to_free = progress.cache.list;
|
|
query_progress_unlink_from_cache_unsafe(qp_to_free);
|
|
query_progress_remove_from_hashtable_unsafe(qp_to_free);
|
|
}
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
|
|
query_progress_free(qp_to_free);
|
|
}
|
|
}
|
|
|
|
void query_progress_functions_update(uuid_t *transaction, size_t done, size_t all) {
|
|
// functions send to the total 'done', not the increment
|
|
|
|
if(!transaction)
|
|
return;
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
|
|
|
|
if(qp) {
|
|
if(all)
|
|
qp->all = all;
|
|
|
|
if(done)
|
|
qp->done = done;
|
|
|
|
qp->updates++;
|
|
}
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// /api/v2/progress - to get the progress of a transaction
|
|
|
|
int web_api_v2_report_progress(uuid_t *transaction, BUFFER *wb) {
|
|
buffer_flush(wb);
|
|
buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
|
|
|
|
if(!transaction) {
|
|
buffer_json_member_add_uint64(wb, "status", 400);
|
|
buffer_json_member_add_string(wb, "message", "No transaction given");
|
|
buffer_json_finalize(wb);
|
|
return 400;
|
|
}
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
QUERY_PROGRESS *qp = query_progress_find_in_hashtable_unsafe(transaction);
|
|
if(!qp) {
|
|
spinlock_unlock(&progress.spinlock);
|
|
buffer_json_member_add_uint64(wb, "status", HTTP_RESP_NOT_FOUND);
|
|
buffer_json_member_add_string(wb, "message", "Transaction not found");
|
|
buffer_json_finalize(wb);
|
|
return HTTP_RESP_NOT_FOUND;
|
|
}
|
|
|
|
buffer_json_member_add_uint64(wb, "status", 200);
|
|
|
|
if(qp->finished_ut) {
|
|
buffer_json_member_add_double(wb, "progress", 100.0);
|
|
buffer_json_member_add_uint64(wb, "age_ut", qp->finished_ut - qp->started_ut);
|
|
}
|
|
else {
|
|
buffer_json_member_add_uint64(wb, "age_ut", now_realtime_usec() - qp->started_ut);
|
|
|
|
if (qp->all)
|
|
buffer_json_member_add_double(wb, "progress", (double) qp->done * 100.0 / (double) qp->all);
|
|
else
|
|
buffer_json_member_add_uint64(wb, "working", qp->done);
|
|
}
|
|
|
|
buffer_json_finalize(wb);
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
|
|
return 200;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// function to show the progress of all current queries
|
|
// and the recent few completed queries
|
|
|
|
int progress_function_result(BUFFER *wb, const char *hostname) {
|
|
buffer_flush(wb);
|
|
wb->content_type = CT_APPLICATION_JSON;
|
|
buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
|
|
|
|
buffer_json_member_add_string(wb, "hostname", hostname);
|
|
buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
|
|
buffer_json_member_add_string(wb, "type", "table");
|
|
buffer_json_member_add_time_t(wb, "update_every", 1);
|
|
buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_PROGRESS_HELP);
|
|
buffer_json_member_add_array(wb, "data");
|
|
|
|
spinlock_lock(&progress.spinlock);
|
|
query_progress_init_unsafe();
|
|
|
|
usec_t now_ut = now_realtime_usec();
|
|
usec_t max_duration_ut = 0;
|
|
size_t max_size = 0, max_sent = 0;
|
|
size_t archived = 0, running = 0;
|
|
SIMPLE_HASHTABLE_FOREACH_READ_ONLY(&progress.hashtable, sl, _QUERY) {
|
|
QUERY_PROGRESS *qp = SIMPLE_HASHTABLE_FOREACH_READ_ONLY_VALUE(sl);
|
|
if(unlikely(!qp)) continue; // not really needed, just for completeness
|
|
|
|
if(qp->prev)
|
|
archived++;
|
|
else
|
|
running++;
|
|
|
|
bool finished = qp->finished_ut ? true : false;
|
|
usec_t duration_ut = finished ? qp->duration_ut : now_ut - qp->started_ut;
|
|
if(duration_ut > max_duration_ut)
|
|
max_duration_ut = duration_ut;
|
|
|
|
if(finished) {
|
|
if(qp->response_size > max_size)
|
|
max_size = qp->response_size;
|
|
|
|
if(qp->sent_size > max_sent)
|
|
max_sent = qp->sent_size;
|
|
}
|
|
|
|
buffer_json_add_array_item_array(wb); // row
|
|
|
|
buffer_json_add_array_item_uuid_compact(wb, &qp->transaction);
|
|
buffer_json_add_array_item_uint64(wb, qp->started_ut);
|
|
buffer_json_add_array_item_string(wb, http_request_method2string(qp->mode));
|
|
buffer_json_add_array_item_string(wb, buffer_tostring(qp->query));
|
|
|
|
if(!buffer_strlen(qp->client)) {
|
|
if(qp->acl & HTTP_ACL_ACLK)
|
|
buffer_json_add_array_item_string(wb, "ACLK");
|
|
else if(qp->acl & HTTP_ACL_WEBRTC)
|
|
buffer_json_add_array_item_string(wb, "WEBRTC");
|
|
else
|
|
buffer_json_add_array_item_string(wb, "unknown");
|
|
}
|
|
else
|
|
buffer_json_add_array_item_string(wb, buffer_tostring(qp->client));
|
|
|
|
if(finished) {
|
|
buffer_json_add_array_item_string(wb, "finished");
|
|
buffer_json_add_array_item_string(wb, "100.00 %%");
|
|
}
|
|
else {
|
|
char buf[50];
|
|
|
|
buffer_json_add_array_item_string(wb, "in-progress");
|
|
|
|
if (qp->all)
|
|
snprintfz(buf, sizeof(buf), "%0.2f %%", (double) qp->done * 100.0 / (double) qp->all);
|
|
else
|
|
snprintfz(buf, sizeof(buf), "%zu", qp->done);
|
|
|
|
buffer_json_add_array_item_string(wb, buf);
|
|
}
|
|
|
|
buffer_json_add_array_item_double(wb, (double)duration_ut / USEC_PER_MS);
|
|
|
|
if(finished) {
|
|
buffer_json_add_array_item_uint64(wb, qp->response_code);
|
|
buffer_json_add_array_item_uint64(wb, qp->response_size);
|
|
buffer_json_add_array_item_uint64(wb, qp->sent_size);
|
|
}
|
|
else {
|
|
buffer_json_add_array_item_string(wb, NULL);
|
|
buffer_json_add_array_item_string(wb, NULL);
|
|
buffer_json_add_array_item_string(wb, NULL);
|
|
}
|
|
|
|
buffer_json_add_array_item_object(wb); // row options
|
|
{
|
|
char *severity = "notice";
|
|
if(finished) {
|
|
if(qp->response_code == HTTP_RESP_NOT_MODIFIED ||
|
|
qp->response_code == HTTP_RESP_CLIENT_CLOSED_REQUEST ||
|
|
qp->response_code == HTTP_RESP_CONFLICT)
|
|
severity = "debug";
|
|
else if(qp->response_code >= 500 && qp->response_code <= 599)
|
|
severity = "error";
|
|
else if(qp->response_code >= 400 && qp->response_code <= 499)
|
|
severity = "warning";
|
|
else if(qp->response_code >= 300 && qp->response_code <= 399)
|
|
severity = "notice";
|
|
else
|
|
severity = "normal";
|
|
}
|
|
buffer_json_member_add_string(wb, "severity", severity);
|
|
}
|
|
buffer_json_object_close(wb); // row options
|
|
|
|
buffer_json_array_close(wb); // row
|
|
}
|
|
|
|
assert(archived == progress.cache.available);
|
|
|
|
spinlock_unlock(&progress.spinlock);
|
|
|
|
buffer_json_array_close(wb); // data
|
|
buffer_json_member_add_object(wb, "columns");
|
|
{
|
|
size_t field_id = 0;
|
|
|
|
// transaction
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Transaction", "Transaction ID",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
|
|
NULL);
|
|
|
|
// timestamp
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Started", "Query Start Timestamp",
|
|
RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_USEC,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// request method
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Method", "Request Method",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// query
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Query", "Query",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_FULL_WIDTH | RRDF_FIELD_OPTS_WRAP, NULL);
|
|
|
|
// client
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Client", "Client",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// status
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Status", "Query Status",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// progress
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Progress", "Query Progress",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// duration
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Duration", "Query Duration",
|
|
RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
|
|
2, "ms", (double)max_duration_ut / USEC_PER_MS, RRDF_FIELD_SORT_DESCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// response code
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Response", "Query Response Code",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE, NULL);
|
|
|
|
// response size
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Size", "Query Response Size",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, "bytes", (double)max_size, RRDF_FIELD_SORT_DESCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
|
|
RRDF_FIELD_OPTS_NONE, NULL);
|
|
|
|
// sent size
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Sent", "Query Response Final Size",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, "bytes", (double)max_sent, RRDF_FIELD_SORT_DESCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
|
|
RRDF_FIELD_OPTS_NONE, NULL);
|
|
|
|
// row options
|
|
buffer_rrdf_table_add_field(wb, field_id++, "rowOptions", "rowOptions",
|
|
RRDF_FIELD_TYPE_NONE, RRDR_FIELD_VISUAL_ROW_OPTIONS, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_FIXED, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_DUMMY, NULL);
|
|
}
|
|
|
|
buffer_json_object_close(wb); // columns
|
|
buffer_json_member_add_string(wb, "default_sort_column", "Started");
|
|
|
|
buffer_json_member_add_time_t(wb, "expires", (time_t)((now_ut / USEC_PER_SEC) + 1));
|
|
buffer_json_finalize(wb);
|
|
|
|
return 200;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
int progress_unittest(void) {
|
|
size_t permanent = 100;
|
|
uuid_t valid[permanent];
|
|
|
|
usec_t started = now_monotonic_usec();
|
|
|
|
for(size_t i = 0; i < permanent ;i++) {
|
|
uuid_generate_random(valid[i]);
|
|
query_progress_start_or_update(&valid[i], 0, HTTP_REQUEST_MODE_GET, HTTP_ACL_ACLK, "permanent", NULL, "test");
|
|
}
|
|
|
|
for(size_t n = 0; n < 5000000 ;n++) {
|
|
uuid_t t;
|
|
uuid_generate_random(t);
|
|
query_progress_start_or_update(&t, 0, HTTP_REQUEST_MODE_OPTIONS, HTTP_ACL_WEBRTC, "ephemeral", NULL, "test");
|
|
query_progress_finished(&t, 0, 200, 1234, 123, 12);
|
|
|
|
QUERY_PROGRESS *qp;
|
|
for(size_t i = 0; i < permanent ;i++) {
|
|
qp = query_progress_find_in_hashtable_unsafe(&valid[i]);
|
|
assert(qp);
|
|
(void)qp;
|
|
}
|
|
}
|
|
|
|
usec_t ended = now_monotonic_usec();
|
|
usec_t duration = ended - started;
|
|
|
|
printf("progress hashtable resizes: %zu, size: %zu, used: %zu, deleted: %zu, searches: %zu, collisions: %zu, additions: %zu, deletions: %zu\n",
|
|
progress.hashtable.resizes,
|
|
progress.hashtable.size, progress.hashtable.used, progress.hashtable.deleted,
|
|
progress.hashtable.searches, progress.hashtable.collisions, progress.hashtable.additions, progress.hashtable.deletions);
|
|
|
|
double d = (double)duration / USEC_PER_SEC;
|
|
printf("hashtable ops: %0.2f / sec\n", (double)progress.hashtable.searches / d);
|
|
|
|
return 0;
|
|
}
|