One way allocator to double the speed of parallel context queries (#12787)

* one way allocator to speed up context queries

* fixed a bug while expanding memory pages

* reworked for clarity and finally fixed the bug of allocating memory beyond the page size

* further optimize allocation step to minimize the number of allocations made

* implement strdup with memcpy instead of strcpy

* added documentation

* prevent an uninitialized use of owa

* added callocz() interface

* integrate onewayalloc everywhere - apart sql queries

* one way allocator is now used in context queries using archived charts in sql

* align on the size of pointers

* forgotten freez()

* removed not needed memcpys

* give unique names to global variables to avoid conflicts with system definitions
This commit is contained in:
Costa Tsaousis 2022-05-03 00:31:19 +03:00 committed by GitHub
parent 47fa3d7089
commit 87c0cc2d60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 389 additions and 95 deletions

View File

@ -383,6 +383,8 @@ set(LIBNETDATA_FILES
libnetdata/log/log.h
libnetdata/os.c
libnetdata/os.h
libnetdata/onewayalloc/onewayalloc.c
libnetdata/onewayalloc/onewayalloc.h
libnetdata/popen/popen.c
libnetdata/popen/popen.h
libnetdata/procfile/procfile.c

View File

@ -158,6 +158,8 @@ LIBNETDATA_FILES = \
libnetdata/locks/locks.h \
libnetdata/log/log.c \
libnetdata/log/log.h \
libnetdata/onewayalloc/onewayalloc.c \
libnetdata/onewayalloc/onewayalloc.h \
libnetdata/popen/popen.c \
libnetdata/popen/popen.h \
libnetdata/procfile/procfile.c \

View File

@ -1758,6 +1758,7 @@ AC_CONFIG_FILES([
libnetdata/eval/Makefile
libnetdata/locks/Makefile
libnetdata/log/Makefile
libnetdata/onewayalloc/Makefile
libnetdata/popen/Makefile
libnetdata/procfile/Makefile
libnetdata/simple_pattern/Makefile

View File

@ -1732,7 +1732,8 @@ static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS]
update_every = REGION_UPDATE_EVERY[current_region];
long points = (time_end - time_start) / update_every;
for (i = 0 ; i < CHARTS ; ++i) {
RRDR *r = rrd2rrdr(st[i], points, time_start + update_every, time_end, RRDR_GROUPING_AVERAGE, 0, 0, NULL, NULL, 0);
ONEWAYALLOC *owa = onewayalloc_create(0);
RRDR *r = rrd2rrdr(owa, st[i], points, time_start + update_every, time_end, RRDR_GROUPING_AVERAGE, 0, 0, NULL, NULL, 0);
if (!r) {
fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name);
return ++errors;
@ -1766,8 +1767,9 @@ static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS]
}
}
}
rrdr_free(r);
rrdr_free(owa, r);
}
onewayalloc_destroy(owa);
}
return errors;
}
@ -1851,7 +1853,8 @@ int test_dbengine(void)
long points = (time_end[REGIONS - 1] - time_start[0]) / update_every; // cover all time regions with RRDR
long point_offset = (time_start[current_region] - time_start[0]) / update_every;
for (i = 0 ; i < CHARTS ; ++i) {
RRDR *r = rrd2rrdr(st[i], points, time_start[0] + update_every, time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, 0, NULL, NULL, 0);
ONEWAYALLOC *owa = onewayalloc_create(0);
RRDR *r = rrd2rrdr(owa, st[i], points, time_start[0] + update_every, time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, 0, NULL, NULL, 0);
if (!r) {
fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name);
++errors;
@ -1888,8 +1891,9 @@ int test_dbengine(void)
}
}
}
rrdr_free(r);
rrdr_free(owa, r);
}
onewayalloc_destroy(owa);
}
error_out:
rrd_wrlock();

View File

@ -1446,13 +1446,13 @@ int find_dimension_first_last_t(char *machine_guid, char *chart_id, char *dim_id
}
#ifdef ENABLE_DBENGINE
static RRDDIM *create_rrdim_entry(RRDSET *st, char *id, char *name, uuid_t *metric_uuid)
static RRDDIM *create_rrdim_entry(ONEWAYALLOC *owa, RRDSET *st, char *id, char *name, uuid_t *metric_uuid)
{
RRDDIM *rd = callocz(1, sizeof(*rd));
RRDDIM *rd = onewayalloc_callocz(owa, 1, sizeof(*rd));
rd->rrdset = st;
rd->last_stored_value = NAN;
rrddim_flag_set(rd, RRDDIM_FLAG_NONE);
rd->state = mallocz(sizeof(*rd->state));
rd->state = onewayalloc_mallocz(owa, sizeof(*rd->state));
rd->rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE;
rd->state->query_ops.init = rrdeng_load_metric_init;
rd->state->query_ops.next_metric = rrdeng_load_metric_next;
@ -1460,11 +1460,11 @@ static RRDDIM *create_rrdim_entry(RRDSET *st, char *id, char *name, uuid_t *metr
rd->state->query_ops.finalize = rrdeng_load_metric_finalize;
rd->state->query_ops.latest_time = rrdeng_metric_latest_time;
rd->state->query_ops.oldest_time = rrdeng_metric_oldest_time;
rd->state->rrdeng_uuid = mallocz(sizeof(uuid_t));
rd->state->rrdeng_uuid = onewayalloc_mallocz(owa, sizeof(uuid_t));
uuid_copy(*rd->state->rrdeng_uuid, *metric_uuid);
uuid_copy(rd->state->metric_uuid, *metric_uuid);
rd->id = strdupz(id);
rd->name = strdupz(name);
rd->id = onewayalloc_strdupz(owa, id);
rd->name = onewayalloc_strdupz(owa, name);
return rd;
}
#endif
@ -1481,7 +1481,7 @@ static RRDDIM *create_rrdim_entry(RRDSET *st, char *id, char *name, uuid_t *metr
"where d.chart_id = c.chart_id and c.host_id = h.host_id and c.host_id = @host_id and c.type||'.'||c.id = @chart " \
"order by c.chart_id asc, c.type||'.'||c.id desc;"
void sql_build_context_param_list(struct context_param **param_list, RRDHOST *host, char *context, char *chart)
void sql_build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDHOST *host, char *context, char *chart)
{
#ifdef ENABLE_DBENGINE
int rc;
@ -1490,7 +1490,7 @@ void sql_build_context_param_list(struct context_param **param_list, RRDHOST *ho
return;
if (unlikely(!(*param_list))) {
*param_list = mallocz(sizeof(struct context_param));
*param_list = onewayalloc_mallocz(owa, sizeof(struct context_param));
(*param_list)->first_entry_t = LONG_MAX;
(*param_list)->last_entry_t = 0;
(*param_list)->rd = NULL;
@ -1539,21 +1539,21 @@ void sql_build_context_param_list(struct context_param **param_list, RRDHOST *ho
if (!st || uuid_compare(*(uuid_t *)sqlite3_column_blob(res, 7), chart_id)) {
if (unlikely(st && !st->counter)) {
freez(st->context);
freez((char *) st->name);
freez(st);
onewayalloc_freez(owa, st->context);
onewayalloc_freez(owa, (char *) st->name);
onewayalloc_freez(owa, st);
}
st = callocz(1, sizeof(*st));
st = onewayalloc_callocz(owa, 1, sizeof(*st));
char n[RRD_ID_LENGTH_MAX + 1];
snprintfz(
n, RRD_ID_LENGTH_MAX, "%s.%s", (char *)sqlite3_column_text(res, 4),
(char *)sqlite3_column_text(res, 3));
st->name = strdupz(n);
st->name = onewayalloc_strdupz(owa, n);
st->update_every = sqlite3_column_int(res, 6);
st->counter = 0;
if (chart) {
st->context = strdupz((char *)sqlite3_column_text(res, 8));
st->context = onewayalloc_strdupz(owa, (char *)sqlite3_column_text(res, 8));
strncpyz(st->id, chart, RRD_ID_LENGTH_MAX);
}
uuid_copy(chart_id, *(uuid_t *)sqlite3_column_blob(res, 7));
@ -1569,7 +1569,7 @@ void sql_build_context_param_list(struct context_param **param_list, RRDHOST *ho
st->counter++;
st->last_entry_t = MAX(st->last_entry_t, (*param_list)->last_entry_t);
RRDDIM *rd = create_rrdim_entry(st, (char *)sqlite3_column_text(res, 1), (char *)sqlite3_column_text(res, 2), &rrdeng_uuid);
RRDDIM *rd = create_rrdim_entry(owa, st, (char *)sqlite3_column_text(res, 1), (char *)sqlite3_column_text(res, 2), &rrdeng_uuid);
if (sqlite3_column_int(res, 9) == 1)
rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
rd->next = (*param_list)->rd;
@ -1577,13 +1577,13 @@ void sql_build_context_param_list(struct context_param **param_list, RRDHOST *ho
}
if (st) {
if (!st->counter) {
freez(st->context);
freez((char *)st->name);
freez(st);
onewayalloc_freez(owa,st->context);
onewayalloc_freez(owa,(char *)st->name);
onewayalloc_freez(owa,st);
}
else
if (!st->context && context)
st->context = strdupz(context);
st->context = onewayalloc_strdupz(owa,context);
}
failed:

View File

@ -89,7 +89,7 @@ extern void db_unlock(void);
extern void db_lock(void);
extern void delete_dimension_uuid(uuid_t *dimension_uuid);
extern void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, char *value);
extern void sql_build_context_param_list(struct context_param **param_list, RRDHOST *host, char *context, char *chart);
extern void sql_build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDHOST *host, char *context, char *chart);
extern void store_claim_id(uuid_t *host_id, uuid_t *claim_id);
extern int update_node_id(uuid_t *host_id, uuid_t *node_id);
extern int get_node_id(uuid_t *host_id, uuid_t *node_id);

View File

@ -17,6 +17,7 @@ SUBDIRS = \
health \
locks \
log \
onewayalloc \
popen \
procfile \
simple_pattern \

View File

@ -345,6 +345,7 @@ extern char *netdata_configured_host_prefix;
#include "json/json.h"
#include "health/health.h"
#include "string/utf8.h"
#include "onewayalloc/onewayalloc.h"
// BEWARE: Outside of the C code this also exists in alarm-notify.sh
#define DEFAULT_CLOUD_BASE_URL "https://app.netdata.cloud"

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later
AUTOMAKE_OPTIONS = subdir-objects
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
dist_noinst_DATA = \
README.md \
$(NULL)

View File

@ -0,0 +1,71 @@
<!--
title: "One Way Allocator"
custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/onewayallocator/README.md
-->
# One Way Allocator
This is a very fast single-threaded-only memory allocator, that minimized system calls
when a lot of memory allocations needs to be made to perform a task, which all of them
can be freed together when the task finishes.
It has been designed to be used for netdata context queries.
For netdata to perform a context query, it builds a virtual chart, a chart that contains
all the dimensions of the charts having the same context. This process requires allocating
several structures for each of the dimensions to attach them to the virtual chart. All
these data can be freed immediately after the query finishes.
## How it works
1. The caller calls `ONEWAYALLOC *owa = onewayalloc_create(sizehint)` to create an OWA.
Internally this allocates the first memory buffer with size >= `sizehint`.
If `sizehint` is zero, it will allocate 1 hardware page (usually 4kb).
No need to check for success or failure. As with `mallocz()` in netdata, a `fatal()`
will be called if the allocation fails - although this will never fail, since Linux
does not really check if there is memory available for `mmap()` calls.
2. The caller can then perform any number of the following calls to acquire memory:
- `onewayalloc_mallocz(owa, size)`, similar to `mallocz()`
- `onewayalloc_callocz(owa, nmemb, size)`, similar to `callocz()`
- `onewayalloc_strdupz(owa, string)`, similar to `strdupz()`
- `onewayalloc_memdupz(owa, ptr, size)`, similar to `mallocz()` and then `memcpy()`
3. Once the caller has done all the work with the allocated buffers, all memory allocated
can be freed with `onewayalloc_destroy(owa)`.
## How faster it is?
On modern hardware, for any single query the performance improvement is marginal and not
noticeable at all.
We performed the following tests using the same huge context query (1000 charts,
100 dimensions each = 100k dimensions)
1. using `mallocz()`, 1 caller, 256 queries (sequential)
2. using `mallocz()`, 256 callers, 1 query each (parallel)
3. using `OWA`, 1 caller, 256 queries (sequential)
4. using `OWA`, 256 callers, 1 query each (parallel)
Netdata was configured to use 24 web threads on the 24 core server we used.
The results are as follows:
### sequential test
branch|transactions|time to complete|transaction rate|average response time|min response time|max response time
:---:|:---:|:---:|:---:|:---:|:---:|:---:|
`malloc()`|256|322.35s|0.79/sec|1.26s|1.01s|1.87s
`OWA`|256|310.19s|0.83/sec|1.21s|1.04s|1.63s
For a single query, the improvement is just marginal and not noticeable at all.
### parallel test
branch|transactions|time to complete|transaction rate|average response time|min response time|max response time
:---:|:---:|:---:|:---:|:---:|:---:|:---:|
`malloc()`|256|84.72s|3.02/sec|68.43s|50.20s|84.71s
`OWA`|256|39.35s|6.51/sec|34.48s|20.55s|39.34s
For parallel workload, like the one executed by netdata.cloud, `OWA` provides a 54% overall speed improvement (more than double the overall
user-experienced speed, including the data query itself).

View File

@ -0,0 +1,173 @@
#include "onewayalloc.h"
static size_t OWA_NATURAL_PAGE_SIZE = 0;
static size_t OWA_NATURAL_ALIGNMENT = sizeof(int*);
typedef struct owa_page {
size_t stats_pages;
size_t stats_pages_size;
size_t stats_mallocs_made;
size_t stats_mallocs_size;
size_t size; // the total size of the page
size_t offset; // the first free byte of the page
struct owa_page *next; // the next page on the list
struct owa_page *last; // the last page on the list - we currently allocate on this
} OWA_PAGE;
// allocations need to be aligned to CPU register width
// https://en.wikipedia.org/wiki/Data_structure_alignment
static inline size_t natural_alignment(size_t size) {
if(unlikely(size % OWA_NATURAL_ALIGNMENT))
size = size + OWA_NATURAL_ALIGNMENT - (size % OWA_NATURAL_ALIGNMENT);
return size;
}
// Create an OWA
// Once it is created, the called may call the onewayalloc_mallocz()
// any number of times, for any amount of memory.
static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
if(unlikely(!OWA_NATURAL_PAGE_SIZE))
OWA_NATURAL_PAGE_SIZE = sysconf(_SC_PAGE_SIZE);
// our default page size
size_t size = OWA_NATURAL_PAGE_SIZE;
// make sure the new page will fit both the requested size
// and the OWA_PAGE structure at its beginning
size_hint += sizeof(OWA_PAGE);
// prefer the user size if it is bigger than our size
if(size_hint > size) size = size_hint;
// try to allocate half of the total we have allocated already
if(likely(head)) {
size_t optimal_size = head->stats_pages_size / 2;
if(optimal_size > size) size = optimal_size;
}
// Make sure our allocations are always a multiple of the hardware page size
if(size % OWA_NATURAL_PAGE_SIZE) size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0);
if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
page->size = size;
page->offset = natural_alignment(sizeof(OWA_PAGE));
page->next = page->last = NULL;
if(unlikely(!head)) {
// this is the first time we are called
head = page;
head->stats_pages = 0;
head->stats_pages_size = 0;
head->stats_mallocs_made = 0;
head->stats_mallocs_size = 0;
}
else {
// link this page into our existing linked list
head->last->next = page;
}
head->last = page;
head->stats_pages++;
head->stats_pages_size += size;
return (ONEWAYALLOC *)page;
}
ONEWAYALLOC *onewayalloc_create(size_t size_hint) {
return onewayalloc_create_internal(NULL, size_hint);
}
void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size) {
OWA_PAGE *head = (OWA_PAGE *)owa;
OWA_PAGE *page = head->last;
// update stats
head->stats_mallocs_made++;
head->stats_mallocs_size += size;
// make sure the size is aligned
size = natural_alignment(size);
if(unlikely(page->size - page->offset < size)) {
// we don't have enough space to fit the data
// let's get another page
page = onewayalloc_create_internal(head, (size > page->size)?size:page->size);
}
char *mem = (char *)page;
mem = &mem[page->offset];
page->offset += size;
return (void *)mem;
}
void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size) {
size_t total = nmemb * size;
void *mem = onewayalloc_mallocz(owa, total);
memset(mem, 0, total);
return mem;
}
char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s) {
size_t size = strlen(s) + 1;
char *d = onewayalloc_mallocz((OWA_PAGE *)owa, size);
memcpy(d, s, size);
return d;
}
void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size) {
void *mem = onewayalloc_mallocz((OWA_PAGE *)owa, size);
// memcpy() is way faster than strcpy() since it does not check for '\0'
memcpy(mem, src, size);
return mem;
}
void onewayalloc_freez(ONEWAYALLOC *owa __maybe_unused, const void *ptr __maybe_unused) {
#ifdef NETDATA_INTERNAL_CHECKS
// allow the caller to call us for a mallocz() allocation
// so try to find it in our memory and if it is not there
// log an error
OWA_PAGE *head = (OWA_PAGE *)owa;
OWA_PAGE *page;
size_t seeking = (size_t)ptr;
for(page = head; page ;page = page->next) {
size_t start = (size_t)page;
size_t end = start + page->size;
if(seeking >= start && seeking <= end) {
// found it - it is ours
// just return to let the caller think we actually did something
return;
}
}
// not found - it is not ours
// let's free it with the system allocator
error("ONEWAYALLOC: request to free address 0x%p that is not allocated by this OWA", ptr);
#endif
return;
}
void onewayalloc_destroy(ONEWAYALLOC *owa) {
if(!owa) return;
OWA_PAGE *head = (OWA_PAGE *)owa;
//info("OWA: %zu allocations of %zu total bytes, in %zu pages of %zu total bytes",
// head->stats_mallocs_made, head->stats_mallocs_size,
// head->stats_pages, head->stats_pages_size);
OWA_PAGE *page = head;
while(page) {
OWA_PAGE *p = page;
page = page->next;
munmap(p, p->size);
}
}

View File

@ -0,0 +1,17 @@
#ifndef ONEWAYALLOC_H
#define ONEWAYALLOC_H 1
#include "../libnetdata.h"
typedef void ONEWAYALLOC;
extern ONEWAYALLOC *onewayalloc_create(size_t size_hint);
extern void onewayalloc_destroy(ONEWAYALLOC *owa);
extern void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size);
extern void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size);
extern char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s);
extern void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size);
extern void onewayalloc_freez(ONEWAYALLOC *owa, const void *ptr);
#endif // ONEWAYALLOC_H

View File

@ -2,27 +2,27 @@
#include "web/api/web_api_v1.h"
static inline void free_single_rrdrim(RRDDIM *temp_rd, int archive_mode)
static inline void free_single_rrdrim(ONEWAYALLOC *owa, RRDDIM *temp_rd, int archive_mode)
{
if (unlikely(!temp_rd))
return;
freez((char *)temp_rd->id);
freez((char *)temp_rd->name);
onewayalloc_freez(owa, (char *)temp_rd->id);
if (unlikely(archive_mode)) {
temp_rd->rrdset->counter--;
if (!temp_rd->rrdset->counter) {
freez((char *)temp_rd->rrdset->name);
freez(temp_rd->rrdset->context);
freez(temp_rd->rrdset);
onewayalloc_freez(owa, (char *)temp_rd->rrdset->name);
onewayalloc_freez(owa, temp_rd->rrdset->context);
onewayalloc_freez(owa, temp_rd->rrdset);
}
}
freez(temp_rd->state);
freez(temp_rd);
onewayalloc_freez(owa, temp_rd->state);
onewayalloc_freez(owa, temp_rd);
}
static inline void free_rrddim_list(RRDDIM *temp_rd, int archive_mode)
static inline void free_rrddim_list(ONEWAYALLOC *owa, RRDDIM *temp_rd, int archive_mode)
{
if (unlikely(!temp_rd))
return;
@ -30,22 +30,22 @@ static inline void free_rrddim_list(RRDDIM *temp_rd, int archive_mode)
RRDDIM *t;
while (temp_rd) {
t = temp_rd->next;
free_single_rrdrim(temp_rd, archive_mode);
free_single_rrdrim(owa, temp_rd, archive_mode);
temp_rd = t;
}
}
void free_context_param_list(struct context_param **param_list)
void free_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list)
{
if (unlikely(!param_list || !*param_list))
return;
free_rrddim_list(((*param_list)->rd), (*param_list)->flags & CONTEXT_FLAGS_ARCHIVE);
freez((*param_list));
free_rrddim_list(owa, ((*param_list)->rd), (*param_list)->flags & CONTEXT_FLAGS_ARCHIVE);
onewayalloc_freez(owa, (*param_list));
*param_list = NULL;
}
void rebuild_context_param_list(struct context_param *context_param_list, time_t after_requested)
void rebuild_context_param_list(ONEWAYALLOC *owa, struct context_param *context_param_list, time_t after_requested)
{
RRDDIM *temp_rd = context_param_list->rd;
RRDDIM *new_rd_list = NULL, *t;
@ -59,19 +59,19 @@ void rebuild_context_param_list(struct context_param *context_param_list, time_t
temp_rd->next = new_rd_list;
new_rd_list = temp_rd;
} else
free_single_rrdrim(temp_rd, is_archived);
free_single_rrdrim(owa, temp_rd, is_archived);
temp_rd = t;
}
context_param_list->rd = new_rd_list;
};
void build_context_param_list(struct context_param **param_list, RRDSET *st)
void build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDSET *st)
{
if (unlikely(!param_list || !st))
return;
if (unlikely(!(*param_list))) {
*param_list = mallocz(sizeof(struct context_param));
*param_list = onewayalloc_mallocz(owa, sizeof(struct context_param));
(*param_list)->first_entry_t = LONG_MAX;
(*param_list)->last_entry_t = 0;
(*param_list)->flags = CONTEXT_FLAGS_CONTEXT;
@ -86,14 +86,10 @@ void build_context_param_list(struct context_param **param_list, RRDSET *st)
(*param_list)->last_entry_t = MAX((*param_list)->last_entry_t, rrdset_last_entry_t_nolock(st));
rrddim_foreach_read(rd1, st) {
RRDDIM *rd = mallocz(rd1->memsize);
memcpy(rd, rd1, rd1->memsize);
rd->id = strdupz(rd1->id);
rd->name = strdupz(rd1->name);
rd->state = mallocz(sizeof(*rd->state));
memcpy(rd->state, rd1->state, sizeof(*rd->state));
memcpy(&rd->state->collect_ops, &rd1->state->collect_ops, sizeof(struct rrddim_collect_ops));
memcpy(&rd->state->query_ops, &rd1->state->query_ops, sizeof(struct rrddim_query_ops));
RRDDIM *rd = onewayalloc_memdupz(owa, rd1, rd1->memsize);
rd->id = onewayalloc_strdupz(owa, rd1->id);
rd->name = onewayalloc_strdupz(owa, rd1->name);
rd->state = onewayalloc_memdupz(owa, rd1->state, sizeof(*rd->state));
rd->next = (*param_list)->rd;
(*param_list)->rd = rd;
}
@ -169,22 +165,27 @@ int rrdset2value_api_v1(
, int *value_is_null
, int timeout
) {
int ret = HTTP_RESP_INTERNAL_SERVER_ERROR;
RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions, NULL, timeout);
ONEWAYALLOC *owa = onewayalloc_create(0);
RRDR *r = rrd2rrdr(owa, st, points, after, before, group_method, group_time, options, dimensions, NULL, timeout);
if(!r) {
if(value_is_null) *value_is_null = 1;
return HTTP_RESP_INTERNAL_SERVER_ERROR;
ret = HTTP_RESP_INTERNAL_SERVER_ERROR;
goto cleanup;
}
if(rrdr_rows(r) == 0) {
rrdr_free(r);
rrdr_free(owa, r);
if(db_after) *db_after = 0;
if(db_before) *db_before = 0;
if(value_is_null) *value_is_null = 1;
return HTTP_RESP_BAD_REQUEST;
ret = HTTP_RESP_BAD_REQUEST;
goto cleanup;
}
if(wb) {
@ -199,13 +200,17 @@ int rrdset2value_api_v1(
long i = (!(options & RRDR_OPTION_REVERSED))?rrdr_rows(r) - 1:0;
*n = rrdr2value(r, i, options, value_is_null, NULL);
ret = HTTP_RESP_OK;
rrdr_free(r);
return HTTP_RESP_OK;
cleanup:
if(r) rrdr_free(owa, r);
onewayalloc_destroy(owa);
return ret;
}
int rrdset2anything_api_v1(
RRDSET *st
ONEWAYALLOC *owa
, RRDSET *st
, BUFFER *wb
, BUFFER *dimensions
, uint32_t format
@ -225,14 +230,14 @@ int rrdset2anything_api_v1(
if (context_param_list && !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE))
st->last_accessed_time = now_realtime_sec();
RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL, context_param_list, timeout);
RRDR *r = rrd2rrdr(owa, st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL, context_param_list, timeout);
if(!r) {
buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
return HTTP_RESP_INTERNAL_SERVER_ERROR;
}
if (r->result_options & RRDR_RESULT_OPTION_CANCEL) {
rrdr_free(r);
rrdr_free(owa, r);
return HTTP_RESP_BACKEND_FETCH_FAILED;
}
@ -411,6 +416,6 @@ int rrdset2anything_api_v1(
break;
}
rrdr_free(r);
rrdr_free(owa, r);
return HTTP_RESP_OK;
}

View File

@ -54,7 +54,8 @@ extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
extern void rrdr_buffer_print_format(BUFFER *wb, uint32_t format);
extern int rrdset2anything_api_v1(
RRDSET *st
ONEWAYALLOC *owa
, RRDSET *st
, BUFFER *wb
, BUFFER *dimensions
, uint32_t format
@ -88,8 +89,8 @@ extern int rrdset2value_api_v1(
, int timeout
);
extern void build_context_param_list(struct context_param **param_list, RRDSET *st);
extern void rebuild_context_param_list(struct context_param *context_param_list, time_t after_requested);
extern void free_context_param_list(struct context_param **param_list);
extern void build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDSET *st);
extern void rebuild_context_param_list(ONEWAYALLOC *owa, struct context_param *context_param_list, time_t after_requested);
extern void free_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list);
#endif /* NETDATA_RRD2JSON_H */

View File

@ -831,7 +831,8 @@ static int rrdr_convert_before_after_to_absolute(
}
static RRDR *rrd2rrdr_fixedstep(
RRDSET *st
ONEWAYALLOC *owa
, RRDSET *st
, long points_requested
, long long after_requested
, long long before_requested
@ -855,7 +856,7 @@ static RRDR *rrd2rrdr_fixedstep(
RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL;
if(duration <= 0 || available_points <= 0)
return rrdr_create(st, 1, context_param_list);
return rrdr_create(owa, st, 1, context_param_list);
// check the number of wanted points in the result
if(unlikely(points_requested < 0)) points_requested = -points_requested;
@ -1013,7 +1014,7 @@ static RRDR *rrd2rrdr_fixedstep(
// initialize our result set
// this also locks the chart for us
RRDR *r = rrdr_create(st, points_wanted, context_param_list);
RRDR *r = rrdr_create(owa, st, points_wanted, context_param_list);
if(unlikely(!r)) {
#ifdef NETDATA_INTERNAL_CHECKS
error("INTERNAL CHECK: Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (uint32_t)duration, points_wanted);
@ -1216,7 +1217,8 @@ static RRDR *rrd2rrdr_fixedstep(
#ifdef ENABLE_DBENGINE
static RRDR *rrd2rrdr_variablestep(
RRDSET *st
ONEWAYALLOC *owa
, RRDSET *st
, long points_requested
, long long after_requested
, long long before_requested
@ -1242,7 +1244,7 @@ static RRDR *rrd2rrdr_variablestep(
if(duration <= 0 || available_points <= 0) {
freez(region_info_array);
return rrdr_create(st, 1, context_param_list);
return rrdr_create(owa, st, 1, context_param_list);
}
// check the number of wanted points in the result
@ -1401,7 +1403,7 @@ static RRDR *rrd2rrdr_variablestep(
// initialize our result set
// this also locks the chart for us
RRDR *r = rrdr_create(st, points_wanted, context_param_list);
RRDR *r = rrdr_create(owa, st, points_wanted, context_param_list);
if(unlikely(!r)) {
#ifdef NETDATA_INTERNAL_CHECKS
error("INTERNAL CHECK: Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (uint32_t)duration, points_wanted);
@ -1608,7 +1610,8 @@ static RRDR *rrd2rrdr_variablestep(
#endif //#ifdef ENABLE_DBENGINE
RRDR *rrd2rrdr(
RRDSET *st
ONEWAYALLOC *owa
, RRDSET *st
, long points_requested
, long long after_requested
, long long before_requested
@ -1644,7 +1647,7 @@ RRDR *rrd2rrdr(
first_entry_t = after_requested;
if (context_param_list && !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)) {
rebuild_context_param_list(context_param_list, after_requested);
rebuild_context_param_list(owa, context_param_list, after_requested);
st = context_param_list->rd ? context_param_list->rd->rrdset : NULL;
if (unlikely(!st))
return NULL;
@ -1669,7 +1672,7 @@ RRDR *rrd2rrdr(
}
freez(region_info_array);
}
return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
return rrd2rrdr_fixedstep(owa, st, points_requested, after_requested, before_requested, group_method,
resampling_time_requested, options, dimensions, rrd_update_every,
first_entry_t, last_entry_t, absolute_period_requested, context_param_list, timeout);
} else {
@ -1680,13 +1683,13 @@ RRDR *rrd2rrdr(
rrd_update_every, first_entry_t,
last_entry_t, options);
}
return rrd2rrdr_variablestep(st, points_requested, after_requested, before_requested, group_method,
return rrd2rrdr_variablestep(owa, st, points_requested, after_requested, before_requested, group_method,
resampling_time_requested, options, dimensions, rrd_update_every,
first_entry_t, last_entry_t, absolute_period_requested, region_info_array, context_param_list, timeout);
}
}
#endif
return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
return rrd2rrdr_fixedstep(owa, st, points_requested, after_requested, before_requested, group_method,
resampling_time_requested, options, dimensions,
rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested, context_param_list, timeout);
}

View File

@ -83,7 +83,7 @@ inline static void rrdr_unlock_rrdset(RRDR *r) {
}
}
inline void rrdr_free(RRDR *r)
inline void rrdr_free(ONEWAYALLOC *owa, RRDR *r)
{
if(unlikely(!r)) {
error("NULL value given!");
@ -91,21 +91,21 @@ inline void rrdr_free(RRDR *r)
}
rrdr_unlock_rrdset(r);
freez(r->t);
freez(r->v);
freez(r->o);
freez(r->od);
freez(r);
onewayalloc_freez(owa, r->t);
onewayalloc_freez(owa, r->v);
onewayalloc_freez(owa, r->o);
onewayalloc_freez(owa, r->od);
onewayalloc_freez(owa, r);
}
RRDR *rrdr_create(struct rrdset *st, long n, struct context_param *context_param_list)
RRDR *rrdr_create(ONEWAYALLOC *owa, struct rrdset *st, long n, struct context_param *context_param_list)
{
if (unlikely(!st)) {
error("NULL value given!");
return NULL;
}
RRDR *r = callocz(1, sizeof(RRDR));
RRDR *r = onewayalloc_callocz(owa, 1, sizeof(RRDR));
r->st = st;
if (!context_param_list || !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)) {
@ -126,10 +126,10 @@ RRDR *rrdr_create(struct rrdset *st, long n, struct context_param *context_param
r->n = n;
r->t = callocz((size_t)n, sizeof(time_t));
r->v = mallocz(n * r->d * sizeof(calculated_number));
r->o = mallocz(n * r->d * sizeof(RRDR_VALUE_FLAGS));
r->od = mallocz(r->d * sizeof(RRDR_DIMENSION_FLAGS));
r->t = onewayalloc_callocz(owa, (size_t)n, sizeof(time_t));
r->v = onewayalloc_mallocz(owa, n * r->d * sizeof(calculated_number));
r->o = onewayalloc_mallocz(owa, n * r->d * sizeof(RRDR_VALUE_FLAGS));
r->od = onewayalloc_mallocz(owa, r->d * sizeof(RRDR_DIMENSION_FLAGS));
// set the hidden flag on hidden dimensions
int c;

View File

@ -102,13 +102,14 @@ typedef struct rrdresult {
#define rrdr_rows(r) ((r)->rows)
#include "database/rrd.h"
extern void rrdr_free(RRDR *r);
extern RRDR *rrdr_create(struct rrdset *st, long n, struct context_param *context_param_list);
extern void rrdr_free(ONEWAYALLOC *owa, RRDR *r);
extern RRDR *rrdr_create(ONEWAYALLOC *owa, struct rrdset *st, long n, struct context_param *context_param_list);
#include "../web_api_v1.h"
#include "web/api/queries/query.h"
extern RRDR *rrd2rrdr(
ONEWAYALLOC *owa,
RRDSET *st, long points_requested, long long after_requested, long long before_requested,
RRDR_GROUPING group_method, long resampling_time_requested, RRDR_OPTIONS options, const char *dimensions,
struct context_param *context_param_list, int timeout);

View File

@ -512,6 +512,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
fix_google_param(outFileName);
RRDSET *st = NULL;
ONEWAYALLOC *owa = onewayalloc_create(0);
if((!chart || !*chart) && (!context)) {
buffer_sprintf(w->response.data, "No chart id is given at the request.");
@ -519,8 +520,10 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
}
struct context_param *context_param_list = NULL;
if (context && !chart) {
RRDSET *st1;
uint32_t context_hash = simple_hash(context);
rrdhost_rdlock(host);
@ -532,14 +535,14 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
(!chart_label_key || rrdset_contains_label_keylist(st1, chart_label_key)) &&
(!chart_labels_filter ||
rrdset_matches_label_keys(st1, chart_labels_filter, words, hash_key_list, &word_count, MAX_CHART_LABELS_FILTER)))
build_context_param_list(&context_param_list, st1);
build_context_param_list(owa, &context_param_list, st1);
}
rrdhost_unlock(host);
if (likely(context_param_list && context_param_list->rd)) // Just set the first one
st = context_param_list->rd->rrdset;
else {
if (!chart_label_key && !chart_labels_filter)
sql_build_context_param_list(&context_param_list, host, context, NULL);
sql_build_context_param_list(owa, &context_param_list, host, context, NULL);
}
}
else {
@ -549,14 +552,14 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
if (likely(st))
st->last_accessed_time = now_realtime_sec();
else
sql_build_context_param_list(&context_param_list, host, NULL, chart);
sql_build_context_param_list(owa, &context_param_list, host, NULL, chart);
}
if (!st) {
if (likely(context_param_list && context_param_list->rd && context_param_list->rd->rrdset))
st = context_param_list->rd->rrdset;
else {
free_context_param_list(&context_param_list);
free_context_param_list(owa, &context_param_list);
context_param_list = NULL;
}
}
@ -630,12 +633,12 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
buffer_strcat(w->response.data, "(");
}
ret = rrdset2anything_api_v1(st, w->response.data, dimensions, format,
ret = rrdset2anything_api_v1(owa, st, w->response.data, dimensions, format,
points, after, before, group, group_time,
options, &last_timestamp_in_data, context_param_list,
chart_label_key, max_anomaly_rates, timeout);
free_context_param_list(&context_param_list);
free_context_param_list(owa, &context_param_list);
if(format == DATASOURCE_DATATABLE_JSONP) {
if(google_timestamp < last_timestamp_in_data)
@ -652,7 +655,8 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
else if(format == DATASOURCE_JSONP)
buffer_strcat(w->response.data, ");");
cleanup:
cleanup:
onewayalloc_destroy(owa);
buffer_free(dimensions);
return ret;
}