@cryptotaxi247 / netdata-1 / commits / b515c7422

Add support for gorilla pages for tier 0. (#15969)

--------- Co-authored-by: Costa Tsaousis <costa@netdata.cloud>

vkalintiris committed Nov 21, 2023 at 21:42 UTC b515c74228cab1ec6de17e003dea8e6cffc9f384
27 files changed +1869 -733
CMakeLists.txt
+4
@@ -797,6 +797,10 @@ set(RRD_PLUGIN_FILES
797 database/engine/metric.h
798 database/engine/pdc.c
799 database/engine/pdc.h
800 + database/engine/page.c
801 + database/engine/page.h
802 + database/engine/page_test.cc
803 + database/engine/page_test.h
804 database/KolmogorovSmirnovDist.c
805 database/KolmogorovSmirnovDist.h
806 )
Makefile.am
+7 -1
@@ -128,6 +128,7 @@ AM_CFLAGS = \
128 $(OPTIONAL_CUPS_CFLAGS) \
129 $(OPTIONAL_XENSTAT_CFLAGS) \
130 $(OPTIONAL_BPF_CFLAGS) \
131 + $(OPTIONAL_GTEST_CFLAGS) \
132 $(NULL)
133
134 sbin_PROGRAMS =
@@ -589,10 +590,14 @@ if ENABLE_DBENGINE
590 database/engine/cache.h \
591 database/engine/metric.c \
592 database/engine/metric.h \
593 + database/engine/page.c \
594 + database/engine/page.h \
595 + database/engine/page_test.cc \
596 + database/engine/page_test.h \
597 database/engine/pdc.c \
598 database/engine/pdc.h \
599 $(NULL)
595 -
600 +
601 RRD_PLUGIN_KSY_BUILTFILES = \
602 database/engine/journalfile_v2.ksy \
603 database/engine/journalfile_v2_virtmemb.ksy \
@@ -1185,6 +1190,7 @@ NETDATA_COMMON_LIBS = \
1190 $(OPTIONAL_YAML_LIBS) \
1191 $(OPTIONAL_ATOMIC_LIBS) \
1192 $(OPTIONAL_DL_LIBS) \
1193 + $(OPTIONAL_GTEST_LIBS) \
1194 $(NULL)
1195
1196 if ENABLE_ACLK
collectors/proc.plugin/sys_devices_pci_aer.c
+2 -2
@@ -240,8 +240,8 @@ int do_proc_sys_devices_pci_aer(int update_every, usec_t dt __maybe_unused) {
240 continue;
241
242 if(!a->st) {
243 - const char *title;
244 - const char *context;
243 + const char *title = "";
244 + const char *context = "";
245
246 switch(a->type) {
247 case AER_DEV_NONFATAL:
configure.ac
+15 -9
@@ -207,6 +207,12 @@ AC_ARG_ENABLE(
207 ,
208 [enable_ml="detect"]
209 )
210 +AC_ARG_ENABLE(
211 + [gtests],
212 + [AS_HELP_STRING([--enable-gtests], [Enable google tests @<:@default no@:>@])],
213 + ,
214 + [enable_gtests="no"]
215 +)
216 AC_ARG_ENABLE(
217 [aclk_ssl_debug],
218 [AS_HELP_STRING([--enable-aclk-ssl-debug], [Enables possibility for SSL key logging @<:@default no@:>@])],
@@ -1432,17 +1438,17 @@ AM_CONDITIONAL([ENABLE_PLUGIN_PERF], [test "${enable_plugin_perf}" = "yes"])
1438 # -----------------------------------------------------------------------------
1439 # gtest/gmock
1440
1435 -AC_MSG_CHECKING([if gtest and gmock can be found])
1441 +if test "${enable_gtests}" = "yes"; then
1442 + AC_MSG_CHECKING([if gtest can be found])
1443
1437 -PKG_CHECK_MODULES([GTEST], [gtest], [have_gtest=yes], [have_gtest=no])
1438 -PKG_CHECK_MODULES([GMOCK], [gmock], [have_gmock=yes], [have_gmock=no])
1444 + PKG_CHECK_MODULES([GTEST], [gtest], [have_gtest=yes], [have_gtest=no])
1445
1440 -if test "${have_gtest}" = "yes" -a "${have_gmock}" = "yes"; then
1441 - OPTIONAL_GTEST_CFLAGS="${GTEST_CFLAGS} ${GMOCK_CFLAGS}"
1442 - OPTIONAL_GTEST_LIBS="${GTEST_LIBS} ${GMOCK_LIBS}"
1443 - have_gtest="yes"
1444 -else
1445 - have_gtest="no"
1446 + if test "${have_gtest}" = "yes"; then
1447 + OPTIONAL_GTEST_CFLAGS="${GTEST_CFLAGS}"
1448 + OPTIONAL_GTEST_LIBS="${GTEST_LIBS}"
1449 +
1450 + AC_DEFINE([HAVE_GTEST], [1], [gtest availability])
1451 + fi
1452 fi
1453
1454 # -----------------------------------------------------------------------------
daemon/global_statistics.c
+90
@@ -65,6 +65,11 @@ static struct global_statistics {
65 uint64_t backfill_queries_made;
66 uint64_t backfill_db_points_read;
67
68 + uint64_t tier0_hot_gorilla_buffers;
69 +
70 + uint64_t tier0_disk_compressed_bytes;
71 + uint64_t tier0_disk_uncompressed_bytes;
72 +
73 uint64_t db_points_stored_per_tier[RRD_STORAGE_TIERS];
74
75 } global_statistics = {
@@ -80,6 +85,10 @@ static struct global_statistics {
85 .api_data_queries_made = 0,
86 .api_data_db_points_read = 0,
87 .api_data_result_points_generated = 0,
88 +
89 + .tier0_hot_gorilla_buffers = 0,
90 + .tier0_disk_compressed_bytes = 0,
91 + .tier0_disk_uncompressed_bytes = 0,
92 };
93
94 void global_statistics_rrdset_done_chart_collection_completed(size_t *points_read_per_tier_array) {
@@ -108,6 +117,18 @@ void global_statistics_backfill_query_completed(size_t points_read) {
117 __atomic_fetch_add(&global_statistics.backfill_db_points_read, points_read, __ATOMIC_RELAXED);
118 }
119
120 +void global_statistics_gorilla_buffer_add_hot() {
121 + __atomic_fetch_add(&global_statistics.tier0_hot_gorilla_buffers, 1, __ATOMIC_RELAXED);
122 +}
123 +
124 +void global_statistics_tier0_disk_compressed_bytes(uint32_t size) {
125 + __atomic_fetch_add(&global_statistics.tier0_disk_compressed_bytes, size, __ATOMIC_RELAXED);
126 +}
127 +
128 +void global_statistics_tier0_disk_uncompressed_bytes(uint32_t size) {
129 + __atomic_fetch_add(&global_statistics.tier0_disk_uncompressed_bytes, size, __ATOMIC_RELAXED);
130 +}
131 +
132 void global_statistics_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source) {
133 switch(query_source) {
134 case QUERY_SOURCE_API_DATA:
@@ -210,6 +231,11 @@ static inline void global_statistics_copy(struct global_statistics *gs, uint8_t
231 gs->backfill_queries_made = __atomic_load_n(&global_statistics.backfill_queries_made, __ATOMIC_RELAXED);
232 gs->backfill_db_points_read = __atomic_load_n(&global_statistics.backfill_db_points_read, __ATOMIC_RELAXED);
233
234 + gs->tier0_hot_gorilla_buffers = __atomic_load_n(&global_statistics.tier0_hot_gorilla_buffers, __ATOMIC_RELAXED);
235 +
236 + gs->tier0_disk_compressed_bytes = __atomic_load_n(&global_statistics.tier0_disk_compressed_bytes, __ATOMIC_RELAXED);
237 + gs->tier0_disk_uncompressed_bytes = __atomic_load_n(&global_statistics.tier0_disk_uncompressed_bytes, __ATOMIC_RELAXED);
238 +
239 for(size_t tier = 0; tier < storage_tiers ;tier++)
240 gs->db_points_stored_per_tier[tier] = __atomic_load_n(&global_statistics.db_points_stored_per_tier[tier], __ATOMIC_RELAXED);
241
@@ -828,6 +854,70 @@ static void global_statistics_charts(void) {
854 }
855
856 ml_update_global_statistics_charts(gs.ml_models_consulted);
857 +
858 + // ----------------------------------------------------------------
859 +
860 + if (tier_page_type[0] == PAGE_GORILLA_METRICS)
861 + {
862 + static RRDSET *st_tier0_gorilla_pages = NULL;
863 + static RRDDIM *rd_num_gorilla_pages = NULL;
864 +
865 + if (unlikely(!st_tier0_gorilla_pages)) {
866 + st_tier0_gorilla_pages = rrdset_create_localhost(
867 + "netdata"
868 + , "tier0_gorilla_pages"
869 + , NULL
870 + , "tier0_gorilla_pages"
871 + , NULL
872 + , "Number of gorilla_pages"
873 + , "count"
874 + , "netdata"
875 + , "stats"
876 + , 131004
877 + , localhost->rrd_update_every
878 + , RRDSET_TYPE_LINE
879 + );
880 +
881 + rd_num_gorilla_pages = rrddim_add(st_tier0_gorilla_pages, "count", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
882 + }
883 +
884 + rrddim_set_by_pointer(st_tier0_gorilla_pages, rd_num_gorilla_pages, (collected_number)gs.tier0_hot_gorilla_buffers);
885 +
886 + rrdset_done(st_tier0_gorilla_pages);
887 + }
888 +
889 + if (tier_page_type[0] == PAGE_GORILLA_METRICS)
890 + {
891 + static RRDSET *st_tier0_compression_info = NULL;
892 +
893 + static RRDDIM *rd_compressed_bytes = NULL;
894 + static RRDDIM *rd_uncompressed_bytes = NULL;
895 +
896 + if (unlikely(!st_tier0_compression_info)) {
897 + st_tier0_compression_info = rrdset_create_localhost(
898 + "netdata"
899 + , "tier0_compression_info"
900 + , NULL
901 + , "tier0_compression_info"
902 + , NULL
903 + , "Tier 0 compression info"
904 + , "bytes"
905 + , "netdata"
906 + , "stats"
907 + , 131005
908 + , localhost->rrd_update_every
909 + , RRDSET_TYPE_LINE
910 + );
911 +
912 + rd_compressed_bytes = rrddim_add(st_tier0_compression_info, "compressed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
913 + rd_uncompressed_bytes = rrddim_add(st_tier0_compression_info, "uncompressed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
914 + }
915 +
916 + rrddim_set_by_pointer(st_tier0_compression_info, rd_compressed_bytes, (collected_number)gs.tier0_disk_compressed_bytes);
917 + rrddim_set_by_pointer(st_tier0_compression_info, rd_uncompressed_bytes, (collected_number)gs.tier0_disk_uncompressed_bytes);
918 +
919 + rrdset_done(st_tier0_compression_info);
920 + }
921 }
922
923 // ----------------------------------------------------------------------------
daemon/global_statistics.h
+5
@@ -45,6 +45,11 @@ void global_statistics_sqlite3_query_completed(bool success, bool busy, bool loc
45 void global_statistics_sqlite3_row_completed(void);
46 void global_statistics_rrdset_done_chart_collection_completed(size_t *points_read_per_tier_array);
47
48 +void global_statistics_gorilla_buffer_add_hot();
49 +
50 +void global_statistics_tier0_disk_compressed_bytes(uint32_t size);
51 +void global_statistics_tier0_disk_uncompressed_bytes(uint32_t size);
52 +
53 void global_statistics_web_request_completed(uint64_t dt,
54 uint64_t bytes_received,
55 uint64_t bytes_sent,
daemon/main.c
+6
@@ -4,6 +4,8 @@
4 #include "buildinfo.h"
5 #include "static_threads.h"
6
7 +#include "database/engine/page_test.h"
8 +
9 #if defined(ENV32BIT)
10 #warning COMPILING 32BIT NETDATA
11 #endif
@@ -1456,6 +1458,10 @@ int main(int argc, char **argv) {
1458 char* stresstest_string = "stresstest=";
1459 #endif
1460
1461 + if(strcmp(optarg, "pgd-tests") == 0) {
1462 + return pgd_test(argc, argv);
1463 + }
1464 +
1465 if(strcmp(optarg, "sqlite-meta-recover") == 0) {
1466 sql_init_database(DB_CHECK_RECOVER, 0);
1467 return 0;
database/engine/cache.c
+4
@@ -1,3 +1,4 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 #include "cache.h"
3
4 /* STATES AND TRANSITIONS
@@ -1861,6 +1862,9 @@ void pgc_destroy(PGC *cache) {
1862 freez(cache->aral);
1863 #endif
1864
1865 + // TODO: @stelfrag/@ktsaou is this correct? address sanitizer says
1866 + // we miss memory without this on shutdown.
1867 + freez(cache->index);
1868 freez(cache);
1869 }
1870 }
database/engine/cache.h
+1
@@ -1,3 +1,4 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 #ifndef DBENGINE_CACHE_H
3 #define DBENGINE_CACHE_H
4
database/engine/metric.c
+1
@@ -1,3 +1,4 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 #include "metric.h"
3
4 typedef int32_t REFCOUNT;
database/engine/metric.h
+1
@@ -1,3 +1,4 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 #ifndef DBENGINE_METRIC_H
3 #define DBENGINE_METRIC_H
4
database/engine/page.c new
+678
@@ -0,0 +1,678 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "page.h"
4 +
5 +#include "libnetdata/libnetdata.h"
6 +
7 +typedef enum __attribute__((packed)) {
8 + PAGE_OPTION_ALL_VALUES_EMPTY = (1 << 0),
9 +} PAGE_OPTIONS;
10 +
11 +typedef enum __attribute__((packed)) {
12 + PGD_STATE_CREATED_FROM_COLLECTOR = (1 << 0),
13 + PGD_STATE_CREATED_FROM_DISK = (1 << 1),
14 + PGD_STATE_SCHEDULED_FOR_FLUSHING = (1 << 2),
15 + PGD_STATE_FLUSHED_TO_DISK = (1 << 3),
16 +} PGD_STATES;
17 +
18 +typedef struct {
19 + uint8_t *data;
20 + uint32_t size;
21 +} page_raw_t;
22 +
23 +
24 +typedef struct {
25 + size_t num_buffers;
26 + gorilla_writer_t *writer;
27 + int aral_index;
28 +} page_gorilla_t;
29 +
30 +struct pgd {
31 + // the page type
32 + uint8_t type;
33 +
34 + // options related to the page
35 + PAGE_OPTIONS options;
36 +
37 + PGD_STATES states;
38 +
39 + // the uses number of slots in the page
40 + uint32_t used;
41 +
42 + // the total number of slots available in the page
43 + uint32_t slots;
44 +
45 + union {
46 + page_raw_t raw;
47 + page_gorilla_t gorilla;
48 + };
49 +};
50 +
51 +// ----------------------------------------------------------------------------
52 +// memory management
53 +
54 +struct {
55 + ARAL *aral_pgd;
56 + ARAL *aral_data[RRD_STORAGE_TIERS];
57 + ARAL *aral_gorilla_buffer[4];
58 + ARAL *aral_gorilla_writer[4];
59 +} pgd_alloc_globals = {};
60 +
61 +static ARAL *pgd_aral_data_lookup(size_t size)
62 +{
63 + for (size_t tier = 0; tier < storage_tiers; tier++)
64 + if (size == tier_page_size[tier])
65 + return pgd_alloc_globals.aral_data[tier];
66 +
67 + return NULL;
68 +}
69 +
70 +void pgd_init_arals(void)
71 +{
72 + // pgd aral
73 + {
74 + char buf[20 + 1];
75 + snprintfz(buf, 20, "pgd");
76 +
77 + // FIXME: add stats
78 + pgd_alloc_globals.aral_pgd = aral_create(
79 + buf,
80 + sizeof(struct pgd),
81 + 64,
82 + 512 * (sizeof(struct pgd)),
83 + pgc_aral_statistics(),
84 + NULL, NULL, false, false);
85 + }
86 +
87 + // tier page aral
88 + {
89 + for (size_t i = storage_tiers; i > 0 ;i--)
90 + {
91 + size_t tier = storage_tiers - i;
92 +
93 + char buf[20 + 1];
94 + snprintfz(buf, 20, "tier%zu-pages", tier);
95 +
96 + pgd_alloc_globals.aral_data[tier] = aral_create(
97 + buf,
98 + tier_page_size[tier],
99 + 64,
100 + 512 * (tier_page_size[tier]),
101 + pgc_aral_statistics(),
102 + NULL, NULL, false, false);
103 + }
104 + }
105 +
106 + // gorilla buffers aral
107 + for (size_t i = 0; i != 4; i++) {
108 + char buf[20 + 1];
109 + snprintfz(buf, 20, "gbuffer-%zu", i);
110 +
111 + // FIXME: add stats
112 + pgd_alloc_globals.aral_gorilla_buffer[i] = aral_create(
113 + buf,
114 + GORILLA_BUFFER_SIZE,
115 + 64,
116 + 512 * GORILLA_BUFFER_SIZE,
117 + pgc_aral_statistics(),
118 + NULL, NULL, false, false);
119 + }
120 +
121 + // gorilla writers aral
122 + for (size_t i = 0; i != 4; i++) {
123 + char buf[20 + 1];
124 + snprintfz(buf, 20, "gwriter-%zu", i);
125 +
126 + // FIXME: add stats
127 + pgd_alloc_globals.aral_gorilla_writer[i] = aral_create(
128 + buf,
129 + sizeof(gorilla_writer_t),
130 + 64,
131 + 512 * sizeof(gorilla_writer_t),
132 + pgc_aral_statistics(),
133 + NULL, NULL, false, false);
134 + }
135 +}
136 +
137 +static void *pgd_data_aral_alloc(size_t size)
138 +{
139 + ARAL *ar = pgd_aral_data_lookup(size);
140 + if (!ar)
141 + return mallocz(size);
142 + else
143 + return aral_mallocz(ar);
144 +}
145 +
146 +static void pgd_data_aral_free(void *page, size_t size)
147 +{
148 + ARAL *ar = pgd_aral_data_lookup(size);
149 + if (!ar)
150 + freez(page);
151 + else
152 + aral_freez(ar, page);
153 +}
154 +
155 +// ----------------------------------------------------------------------------
156 +// management api
157 +
158 +PGD *pgd_create(uint8_t type, uint32_t slots)
159 +{
160 + PGD *pg = aral_mallocz(pgd_alloc_globals.aral_pgd);
161 + pg->type = type;
162 + pg->used = 0;
163 + pg->slots = slots;
164 + pg->options = PAGE_OPTION_ALL_VALUES_EMPTY;
165 + pg->states = PGD_STATE_CREATED_FROM_COLLECTOR;
166 +
167 + switch (type) {
168 + case PAGE_METRICS:
169 + case PAGE_TIER: {
170 + uint32_t size = slots * page_type_size[type];
171 +
172 + internal_fatal(!size || slots == 1,
173 + "DBENGINE: invalid number of slots (%u) or page type (%u)", slots, type);
174 +
175 + pg->raw.size = size;
176 + pg->raw.data = pgd_data_aral_alloc(size);
177 + break;
178 + }
179 + case PAGE_GORILLA_METRICS: {
180 + internal_fatal(slots == 1,
181 + "DBENGINE: invalid number of slots (%u) or page type (%u)", slots, type);
182 +
183 + pg->slots = 8 * GORILLA_BUFFER_SLOTS;
184 +
185 + // allocate new gorilla writer
186 + pg->gorilla.aral_index = gettid() % 4;
187 + pg->gorilla.writer = aral_mallocz(pgd_alloc_globals.aral_gorilla_writer[pg->gorilla.aral_index]);
188 +
189 + // allocate new gorilla buffer
190 + gorilla_buffer_t *gbuf = aral_mallocz(pgd_alloc_globals.aral_gorilla_buffer[pg->gorilla.aral_index]);
191 + memset(gbuf, 0, GORILLA_BUFFER_SIZE);
192 + global_statistics_gorilla_buffer_add_hot();
193 +
194 + *pg->gorilla.writer = gorilla_writer_init(gbuf, GORILLA_BUFFER_SLOTS);
195 + pg->gorilla.num_buffers = 1;
196 +
197 + break;
198 + }
199 + default:
200 + fatal("Unknown page type: %uc", type);
201 + }
202 +
203 + return pg;
204 +}
205 +
206 +PGD *pgd_create_from_disk_data(uint8_t type, void *base, uint32_t size)
207 +{
208 + if (!size)
209 + return PGD_EMPTY;
210 +
211 + if (size < page_type_size[type])
212 + return PGD_EMPTY;
213 +
214 + PGD *pg = aral_mallocz(pgd_alloc_globals.aral_pgd);
215 +
216 + pg->type = type;
217 + pg->states = PGD_STATE_CREATED_FROM_DISK;
218 + pg->options = ~PAGE_OPTION_ALL_VALUES_EMPTY;
219 +
220 + switch (type)
221 + {
222 + case PAGE_METRICS:
223 + case PAGE_TIER:
224 + pg->raw.size = size;
225 + pg->used = size / page_type_size[type];
226 + pg->slots = pg->used;
227 +
228 + pg->raw.data = pgd_data_aral_alloc(size);
229 + memcpy(pg->raw.data, base, size);
230 + break;
231 + case PAGE_GORILLA_METRICS:
232 + internal_fatal(size == 0, "Asked to create page with 0 data!!!");
233 + internal_fatal(size % sizeof(uint32_t), "Unaligned gorilla buffer size");
234 + internal_fatal(size % GORILLA_BUFFER_SIZE, "Expected size to be a multiple of %zu-bytes", GORILLA_BUFFER_SIZE);
235 +
236 + pg->raw.data = mallocz(size);
237 + pg->raw.size = size;
238 +
239 + // TODO: rm this
240 + memset(pg->raw.data, 0, size);
241 + memcpy(pg->raw.data, base, size);
242 +
243 + uint32_t total_entries = gorilla_buffer_patch((void *) pg->raw.data);
244 +
245 + pg->used = total_entries;
246 + pg->slots = pg->used;
247 + break;
248 + default:
249 + fatal("Unknown page type: %uc", type);
250 + }
251 +
252 + return pg;
253 +}
254 +
255 +void pgd_free(PGD *pg)
256 +{
257 + if (!pg)
258 + return;
259 +
260 + if (pg == PGD_EMPTY)
261 + return;
262 +
263 + switch (pg->type)
264 + {
265 + case PAGE_METRICS:
266 + case PAGE_TIER:
267 + pgd_data_aral_free(pg->raw.data, pg->raw.size);
268 + break;
269 + case PAGE_GORILLA_METRICS: {
270 + if (pg->states & PGD_STATE_CREATED_FROM_DISK)
271 + {
272 + internal_fatal(pg->raw.data == NULL, "Tried to free gorilla PGD loaded from disk with NULL data");
273 + freez(pg->raw.data);
274 + pg->raw.data = NULL;
275 + }
276 + else if ((pg->states & PGD_STATE_CREATED_FROM_COLLECTOR) ||
277 + (pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING) ||
278 + (pg->states & PGD_STATE_FLUSHED_TO_DISK))
279 + {
280 + internal_fatal(pg->gorilla.writer == NULL,
281 + "PGD does not have an active gorilla writer");
282 +
283 + internal_fatal(pg->gorilla.num_buffers == 0,
284 + "PGD does not have any gorilla buffers allocated");
285 +
286 + while (true) {
287 + gorilla_buffer_t *gbuf = gorilla_writer_drop_head_buffer(pg->gorilla.writer);
288 + if (!gbuf)
289 + break;
290 + aral_freez(pgd_alloc_globals.aral_gorilla_buffer[pg->gorilla.aral_index], gbuf);
291 + pg->gorilla.num_buffers -= 1;
292 + }
293 +
294 + internal_fatal(pg->gorilla.num_buffers != 0,
295 + "Could not free all gorilla writer buffers");
296 +
297 + aral_freez(pgd_alloc_globals.aral_gorilla_writer[pg->gorilla.aral_index], pg->gorilla.writer);
298 + pg->gorilla.writer = NULL;
299 + } else {
300 + fatal("pgd_free() called on gorilla page with unsupported state");
301 + // TODO: should we support any other states?
302 + // if (!(pg->states & PGD_STATE_FLUSHED_TO_DISK))
303 + // fatal("pgd_free() is not supported yet for pages flushed to disk");
304 + }
305 +
306 + break;
307 + }
308 + default:
309 + fatal("Unknown page type: %uc", pg->type);
310 + }
311 +
312 + aral_freez(pgd_alloc_globals.aral_pgd, pg);
313 +}
314 +
315 +// ----------------------------------------------------------------------------
316 +// utility functions
317 +
318 +uint32_t pgd_type(PGD *pg)
319 +{
320 + return pg->type;
321 +}
322 +
323 +bool pgd_is_empty(PGD *pg)
324 +{
325 + if (!pg)
326 + return true;
327 +
328 + if (pg == PGD_EMPTY)
329 + return true;
330 +
331 + if (pg->used == 0)
332 + return true;
333 +
334 + if (pg->options & PAGE_OPTION_ALL_VALUES_EMPTY)
335 + return true;
336 +
337 + return false;
338 +}
339 +
340 +uint32_t pgd_slots_used(PGD *pg)
341 +{
342 + if (!pg)
343 + return 0;
344 +
345 + if (pg == PGD_EMPTY)
346 + return 0;
347 +
348 + return pg->used;
349 +}
350 +
351 +uint32_t pgd_memory_footprint(PGD *pg)
352 +{
353 + if (!pg)
354 + return 0;
355 +
356 + if (pg == PGD_EMPTY)
357 + return 0;
358 +
359 + size_t footprint = 0;
360 + switch (pg->type) {
361 + case PAGE_METRICS:
362 + case PAGE_TIER:
363 + footprint = sizeof(PGD) + pg->raw.size;
364 + break;
365 + case PAGE_GORILLA_METRICS: {
366 + if (pg->states & PGD_STATE_CREATED_FROM_DISK)
367 + footprint = sizeof(PGD) + pg->raw.size;
368 + else
369 + footprint = sizeof(PGD) + sizeof(gorilla_writer_t) + (pg->gorilla.num_buffers * GORILLA_BUFFER_SIZE);
370 +
371 + break;
372 + }
373 + default:
374 + fatal("Unknown page type: %uc", pg->type);
375 + }
376 +
377 + return footprint;
378 +}
379 +
380 +uint32_t pgd_disk_footprint(PGD *pg)
381 +{
382 + if (!pgd_slots_used(pg))
383 + return 0;
384 +
385 + size_t size = 0;
386 +
387 + switch (pg->type) {
388 + case PAGE_METRICS:
389 + case PAGE_TIER: {
390 + uint32_t used_size = pg->used * page_type_size[pg->type];
391 + internal_fatal(used_size > pg->raw.size, "Wrong disk footprint page size");
392 + size = used_size;
393 +
394 + break;
395 + }
396 + case PAGE_GORILLA_METRICS: {
397 + if (pg->states & PGD_STATE_CREATED_FROM_COLLECTOR ||
398 + pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING ||
399 + pg->states & PGD_STATE_FLUSHED_TO_DISK)
400 + {
401 + internal_fatal(!pg->gorilla.writer,
402 + "pgd_disk_footprint() not implemented for NULL gorilla writers");
403 +
404 + internal_fatal(pg->gorilla.num_buffers == 0,
405 + "Gorilla writer does not have any buffers");
406 +
407 + size = pg->gorilla.num_buffers * GORILLA_BUFFER_SIZE;
408 +
409 + if (pg->states & PGD_STATE_CREATED_FROM_COLLECTOR) {
410 + global_statistics_tier0_disk_compressed_bytes(gorilla_writer_nbytes(pg->gorilla.writer));
411 + global_statistics_tier0_disk_uncompressed_bytes(gorilla_writer_entries(pg->gorilla.writer) * sizeof(storage_number));
412 + }
413 + } else if (pg->states & PGD_STATE_CREATED_FROM_DISK) {
414 + size = pg->raw.size;
415 + } else {
416 + fatal("Asked disk footprint on unknown page state");
417 + }
418 +
419 + break;
420 + }
421 + default:
422 + fatal("Unknown page type: %uc", pg->type);
423 + }
424 +
425 + internal_fatal(pg->states & PGD_STATE_CREATED_FROM_DISK,
426 + "Disk footprint asked for page created from disk.");
427 + pg->states = PGD_STATE_SCHEDULED_FOR_FLUSHING;
428 + return size;
429 +}
430 +
431 +void pgd_copy_to_extent(PGD *pg, uint8_t *dst, uint32_t dst_size)
432 +{
433 + internal_fatal(pgd_disk_footprint(pg) != dst_size, "Wrong disk footprint size requested (need %u, available %u)",
434 + pgd_disk_footprint(pg), dst_size);
435 +
436 + switch (pg->type) {
437 + case PAGE_METRICS:
438 + case PAGE_TIER:
439 + memcpy(dst, pg->raw.data, dst_size);
440 + break;
441 + case PAGE_GORILLA_METRICS: {
442 + if ((pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING) == 0)
443 + fatal("Copying to extent is supported only for PGDs that are scheduled for flushing.");
444 +
445 + internal_fatal(!pg->gorilla.writer,
446 + "pgd_copy_to_extent() not implemented for NULL gorilla writers");
447 +
448 + internal_fatal(pg->gorilla.num_buffers == 0,
449 + "pgd_copy_to_extent() gorilla writer does not have any buffers");
450 +
451 + bool ok = gorilla_writer_serialize(pg->gorilla.writer, dst, dst_size);
452 + internal_fatal(!ok,
453 + "pgd_copy_to_extent() tried to serialize pg=%p, gw=%p (with dst_size=%u bytes, num_buffers=%zu)",
454 + pg, pg->gorilla.writer, dst_size, pg->gorilla.num_buffers);
455 + break;
456 + }
457 + default:
458 + fatal("Unknown page type: %uc", pg->type);
459 + }
460 +
461 + pg->states = PGD_STATE_FLUSHED_TO_DISK;
462 +}
463 +
464 +// ----------------------------------------------------------------------------
465 +// data collection
466 +
467 +void pgd_append_point(PGD *pg,
468 + usec_t point_in_time_ut __maybe_unused,
469 + NETDATA_DOUBLE n,
470 + NETDATA_DOUBLE min_value,
471 + NETDATA_DOUBLE max_value,
472 + uint16_t count,
473 + uint16_t anomaly_count,
474 + SN_FLAGS flags,
475 + uint32_t expected_slot)
476 +{
477 + if (unlikely(pg->used >= pg->slots))
478 + fatal("DBENGINE: attempted to write beyond page size (page type %u, slots %u, used %u)",
479 + pg->type, pg->slots, pg->used /* FIXME:, pg->size */);
480 +
481 + if (unlikely(pg->used != expected_slot))
482 + fatal("DBENGINE: page is not aligned to expected slot (used %u, expected %u)",
483 + pg->used, expected_slot);
484 +
485 + if (!(pg->states & PGD_STATE_CREATED_FROM_COLLECTOR))
486 + fatal("DBENGINE: collection on page not created from a collector");
487 +
488 + if (pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING)
489 + fatal("Data collection on page already scheduled for flushing");
490 +
491 + switch (pg->type) {
492 + case PAGE_METRICS: {
493 + storage_number *tier0_metric_data = (storage_number *)pg->raw.data;
494 + storage_number t = pack_storage_number(n, flags);
495 + tier0_metric_data[pg->used++] = t;
496 +
497 + if ((pg->options & PAGE_OPTION_ALL_VALUES_EMPTY) && does_storage_number_exist(t))
498 + pg->options &= ~PAGE_OPTION_ALL_VALUES_EMPTY;
499 +
500 + break;
501 + }
502 + case PAGE_TIER: {
503 + storage_number_tier1_t *tier12_metric_data = (storage_number_tier1_t *)pg->raw.data;
504 + storage_number_tier1_t t;
505 + t.sum_value = (float) n;
506 + t.min_value = (float) min_value;
507 + t.max_value = (float) max_value;
508 + t.anomaly_count = anomaly_count;
509 + t.count = count;
510 + tier12_metric_data[pg->used++] = t;
511 +
512 + if ((pg->options & PAGE_OPTION_ALL_VALUES_EMPTY) && fpclassify(n) != FP_NAN)
513 + pg->options &= ~PAGE_OPTION_ALL_VALUES_EMPTY;
514 +
515 + break;
516 + }
517 + case PAGE_GORILLA_METRICS: {
518 + pg->used++;
519 + storage_number t = pack_storage_number(n, flags);
520 +
521 + if ((pg->options & PAGE_OPTION_ALL_VALUES_EMPTY) && does_storage_number_exist(t))
522 + pg->options &= ~PAGE_OPTION_ALL_VALUES_EMPTY;
523 +
524 + bool ok = gorilla_writer_write(pg->gorilla.writer, t);
525 + if (!ok) {
526 + gorilla_buffer_t *new_buffer = aral_mallocz(pgd_alloc_globals.aral_gorilla_buffer[pg->gorilla.aral_index]);
527 + memset(new_buffer, 0, GORILLA_BUFFER_SIZE);
528 +
529 + gorilla_writer_add_buffer(pg->gorilla.writer, new_buffer, GORILLA_BUFFER_SLOTS);
530 + pg->gorilla.num_buffers += 1;
531 + global_statistics_gorilla_buffer_add_hot();
532 +
533 + ok = gorilla_writer_write(pg->gorilla.writer, t);
534 + internal_fatal(ok == false, "Failed to writer value in newly allocated gorilla buffer.");
535 + }
536 + break;
537 + }
538 + default:
539 + fatal("DBENGINE: unknown page type id %d", pg->type);
540 + break;
541 + }
542 +}
543 +
544 +// ----------------------------------------------------------------------------
545 +// querying with cursor
546 +
547 +static void pgdc_seek(PGDC *pgdc, uint32_t position)
548 +{
549 + PGD *pg = pgdc->pgd;
550 +
551 + switch (pg->type) {
552 + case PAGE_METRICS:
553 + case PAGE_TIER:
554 + pgdc->slots = pgdc->pgd->used;
555 + break;
556 + case PAGE_GORILLA_METRICS: {
557 + if (pg->states & PGD_STATE_CREATED_FROM_DISK) {
558 + pgdc->slots = pgdc->pgd->slots;
559 + pgdc->gr = gorilla_reader_init((void *) pg->raw.data);
560 + } else {
561 + if (!(pg->states & PGD_STATE_CREATED_FROM_COLLECTOR) &&
562 + !(pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING) &&
563 + !(pg->states & PGD_STATE_FLUSHED_TO_DISK))
564 + fatal("pgdc_seek() currently is not supported for pages created from disk.");
565 +
566 + if (!pg->gorilla.writer)
567 + fatal("Seeking from a page without an active gorilla writer is not supported (yet).");
568 +
569 + pgdc->slots = gorilla_writer_entries(pg->gorilla.writer);
570 + pgdc->gr = gorilla_writer_get_reader(pg->gorilla.writer);
571 + }
572 +
573 + if (position > pgdc->slots)
574 + position = pgdc->slots;
575 +
576 + for (uint32_t i = 0; i != position; i++) {
577 + uint32_t value;
578 +
579 + bool ok = gorilla_reader_read(&pgdc->gr, &value);
580 +
581 + if (!ok) {
582 + // this is fine, the reader will return empty points
583 + break;
584 + }
585 + }
586 +
587 + break;
588 + }
589 + default:
590 + fatal("DBENGINE: unknown page type id %d", pg->type);
591 + break;
592 + }
593 +}
594 +
595 +void pgdc_reset(PGDC *pgdc, PGD *pgd, uint32_t position)
596 +{
597 + // pgd might be null and position equal to UINT32_MAX
598 +
599 + pgdc->pgd = pgd;
600 + pgdc->position = position;
601 +
602 + if (!pgd)
603 + return;
604 +
605 + if (pgd == PGD_EMPTY)
606 + return;
607 +
608 + if (position == UINT32_MAX)
609 + return;
610 +
611 + pgdc_seek(pgdc, position);
612 +}
613 +
614 +bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT *sp)
615 +{
616 + if (!pgdc->pgd || pgdc->pgd == PGD_EMPTY || pgdc->position >= pgdc->slots)
617 + {
618 + storage_point_empty(*sp, sp->start_time_s, sp->end_time_s);
619 + return false;
620 + }
621 +
622 + internal_fatal(pgdc->position != expected_position, "Wrong expected cursor position");
623 +
624 + switch (pgdc->pgd->type)
625 + {
626 + case PAGE_METRICS: {
627 + storage_number *array = (storage_number *) pgdc->pgd->raw.data;
628 + storage_number n = array[pgdc->position++];
629 +
630 + sp->min = sp->max = sp->sum = unpack_storage_number(n);
631 + sp->flags = (SN_FLAGS)(n & SN_USER_FLAGS);
632 + sp->count = 1;
633 + sp->anomaly_count = is_storage_number_anomalous(n) ? 1 : 0;
634 +
635 + return true;
636 + }
637 + case PAGE_TIER: {
638 + storage_number_tier1_t *array = (storage_number_tier1_t *) pgdc->pgd->raw.data;
639 + storage_number_tier1_t n = array[pgdc->position++];
640 +
641 + sp->flags = n.anomaly_count ? SN_FLAG_NONE : SN_FLAG_NOT_ANOMALOUS;
642 + sp->count = n.count;
643 + sp->anomaly_count = n.anomaly_count;
644 + sp->min = n.min_value;
645 + sp->max = n.max_value;
646 + sp->sum = n.sum_value;
647 +
648 + return true;
649 + }
650 + case PAGE_GORILLA_METRICS: {
651 + pgdc->position++;
652 +
653 + uint32_t n = 666666666;
654 + bool ok = gorilla_reader_read(&pgdc->gr, &n);
655 + if (ok) {
656 + sp->min = sp->max = sp->sum = unpack_storage_number(n);
657 + sp->flags = (SN_FLAGS)(n & SN_USER_FLAGS);
658 + sp->count = 1;
659 + sp->anomaly_count = is_storage_number_anomalous(n) ? 1 : 0;
660 + } else {
661 + storage_point_empty(*sp, sp->start_time_s, sp->end_time_s);
662 + }
663 +
664 + return ok;
665 + }
666 + default: {
667 + static bool logged = false;
668 + if (!logged)
669 + {
670 + netdata_log_error("DBENGINE: unknown page type %d found. Cannot decode it. Ignoring its metrics.", pgd_type(pgdc->pgd));
671 + logged = true;
672 + }
673 +
674 + storage_point_empty(*sp, sp->start_time_s, sp->end_time_s);
675 + return false;
676 + }
677 + }
678 +}
database/engine/page.h new
+58
@@ -0,0 +1,58 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef DBENGINE_PAGE_H
4 +#define DBENGINE_PAGE_H
5 +
6 +#ifdef __cplusplus
7 +extern "C" {
8 +#endif
9 +
10 +#include "libnetdata/libnetdata.h"
11 +
12 +typedef struct pgd_cursor {
13 + struct pgd *pgd;
14 + uint32_t position;
15 + uint32_t slots;
16 +
17 + gorilla_reader_t gr;
18 +} PGDC;
19 +
20 +#include "rrdengine.h"
21 +
22 +typedef struct pgd PGD;
23 +
24 +#define PGD_EMPTY (PGD *)(-1)
25 +
26 +void pgd_init_arals(void);
27 +
28 +PGD *pgd_create(uint8_t type, uint32_t slots);
29 +PGD *pgd_create_from_disk_data(uint8_t type, void *base, uint32_t size);
30 +void pgd_free(PGD *pg);
31 +
32 +uint32_t pgd_type(PGD *pg);
33 +bool pgd_is_empty(PGD *pg);
34 +uint32_t pgd_slots_used(PGD *pg);
35 +
36 +uint32_t pgd_memory_footprint(PGD *pg);
37 +uint32_t pgd_disk_footprint(PGD *pg);
38 +
39 +void pgd_copy_to_extent(PGD *pg, uint8_t *dst, uint32_t dst_size);
40 +
41 +void pgd_append_point(PGD *pg,
42 + usec_t point_in_time_ut,
43 + NETDATA_DOUBLE n,
44 + NETDATA_DOUBLE min_value,
45 + NETDATA_DOUBLE max_value,
46 + uint16_t count,
47 + uint16_t anomaly_count,
48 + SN_FLAGS flags,
49 + uint32_t expected_slot);
50 +
51 +void pgdc_reset(PGDC *pgdc, PGD *pgd, uint32_t position);
52 +bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT *sp);
53 +
54 +#ifdef __cplusplus
55 +}
56 +#endif
57 +
58 +#endif // DBENGINE_PAGE_H
database/engine/page_test.cc new
+405
@@ -0,0 +1,405 @@
1 +#include "page.h"
2 +#include "page_test.h"
3 +
4 +#ifdef HAVE_GTEST
5 +
6 +#include <gtest/gtest.h>
7 +#include <limits>
8 +#include <random>
9 +
10 +bool operator==(const STORAGE_POINT lhs, const STORAGE_POINT rhs) {
11 + if (lhs.min != rhs.min)
12 + return false;
13 +
14 + if (lhs.max != rhs.max)
15 + return false;
16 +
17 + if (lhs.sum != rhs.sum)
18 + return false;
19 +
20 + if (lhs.start_time_s != rhs.start_time_s)
21 + return false;
22 +
23 + if (lhs.end_time_s != rhs.end_time_s)
24 + return false;
25 +
26 + if (lhs.count != rhs.count)
27 + return false;
28 +
29 + if (lhs.flags != rhs.flags)
30 + return false;
31 +
32 + return true;
33 +}
34 +
35 +// TODO: use value-parameterized tests
36 +// http://google.github.io/googletest/advanced.html#value-parameterized-tests
37 +static uint8_t page_type = PAGE_GORILLA_METRICS;
38 +
39 +static size_t slots_for_page(size_t n) {
40 + switch (page_type) {
41 + case PAGE_METRICS:
42 + return 1024;
43 + case PAGE_GORILLA_METRICS:
44 + return n;
45 + default:
46 + fatal("Slots requested for unsupported page: %uc", page_type);
47 + }
48 +}
49 +
50 +TEST(PGD, EmptyOrNull) {
51 + PGD *pg = NULL;
52 +
53 + PGDC cursor;
54 + STORAGE_POINT sp;
55 +
56 + EXPECT_TRUE(pgd_is_empty(pg));
57 + EXPECT_EQ(pgd_slots_used(pg), 0);
58 + EXPECT_EQ(pgd_memory_footprint(pg), 0);
59 + EXPECT_EQ(pgd_disk_footprint(pg), 0);
60 +
61 + pgdc_reset(&cursor, pg, 0);
62 + EXPECT_FALSE(pgdc_get_next_point(&cursor, 0, &sp));
63 +
64 + pgd_free(pg);
65 +
66 + pg = PGD_EMPTY;
67 +
68 + EXPECT_TRUE(pgd_is_empty(pg));
69 + EXPECT_EQ(pgd_slots_used(pg), 0);
70 + EXPECT_EQ(pgd_memory_footprint(pg), 0);
71 + EXPECT_EQ(pgd_disk_footprint(pg), 0);
72 + EXPECT_FALSE(pgdc_get_next_point(&cursor, 0, &sp));
73 +
74 + pgdc_reset(&cursor, pg, 0);
75 + EXPECT_FALSE(pgdc_get_next_point(&cursor, 0, &sp));
76 +
77 + pgd_free(pg);
78 +}
79 +
80 +TEST(PGD, Create) {
81 + size_t slots = slots_for_page(1024 * 1024);
82 + PGD *pg = pgd_create(page_type, slots);
83 +
84 + EXPECT_EQ(pgd_type(pg), page_type);
85 + EXPECT_TRUE(pgd_is_empty(pg));
86 + EXPECT_EQ(pgd_slots_used(pg), 0);
87 +
88 + for (size_t i = 0; i != slots; i++) {
89 + pgd_append_point(pg, i, i, 0, 0, 1, 1, SN_DEFAULT_FLAGS, i);
90 + EXPECT_FALSE(pgd_is_empty(pg));
91 + }
92 + EXPECT_EQ(pgd_slots_used(pg), slots);
93 +
94 + EXPECT_DEATH(
95 + pgd_append_point(pg, slots, slots, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slots),
96 + ".*"
97 + );
98 +
99 + pgd_free(pg);
100 +}
101 +
102 +TEST(PGD, CursorFullPage) {
103 + size_t slots = slots_for_page(1024 * 1024);
104 + PGD *pg = pgd_create(page_type, slots);
105 +
106 + for (size_t slot = 0; slot != slots; slot++)
107 + pgd_append_point(pg, slot, slot, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slot);
108 +
109 + for (size_t i = 0; i != 2; i++) {
110 + PGDC cursor;
111 + pgdc_reset(&cursor, pg, 0);
112 +
113 + STORAGE_POINT sp;
114 + for (size_t slot = 0; slot != slots; slot++) {
115 + EXPECT_TRUE(pgdc_get_next_point(&cursor, slot, &sp));
116 +
117 + EXPECT_EQ(slot, static_cast<size_t>(sp.min));
118 + EXPECT_EQ(sp.min, sp.max);
119 + EXPECT_EQ(sp.min, sp.sum);
120 + EXPECT_EQ(sp.count, 1);
121 + EXPECT_EQ(sp.anomaly_count, 0);
122 + }
123 +
124 + EXPECT_FALSE(pgdc_get_next_point(&cursor, slots, &sp));
125 + }
126 +
127 + for (size_t i = 0; i != 2; i++) {
128 + PGDC cursor;
129 + pgdc_reset(&cursor, pg, slots / 2);
130 +
131 + STORAGE_POINT sp;
132 + for (size_t slot = slots / 2; slot != slots; slot++) {
133 + EXPECT_TRUE(pgdc_get_next_point(&cursor, slot, &sp));
134 +
135 + EXPECT_EQ(slot, static_cast<size_t>(sp.min));
136 + EXPECT_EQ(sp.min, sp.max);
137 + EXPECT_EQ(sp.min, sp.sum);
138 + EXPECT_EQ(sp.count, 1);
139 + EXPECT_EQ(sp.anomaly_count, 0);
140 + }
141 +
142 + EXPECT_FALSE(pgdc_get_next_point(&cursor, slots, &sp));
143 + }
144 +
145 + // out of bounds seek
146 + {
147 + PGDC cursor;
148 + pgdc_reset(&cursor, pg, 2 * slots);
149 +
150 + STORAGE_POINT sp;
151 + EXPECT_FALSE(pgdc_get_next_point(&cursor, 2 * slots, &sp));
152 + }
153 +
154 + pgd_free(pg);
155 +}
156 +
157 +TEST(PGD, CursorHalfPage) {
158 + size_t slots = slots_for_page(1024 * 1024);
159 + PGD *pg = pgd_create(page_type, slots);
160 +
161 + PGDC cursor;
162 + STORAGE_POINT sp;
163 +
164 + // fill the 1st half of the page
165 + for (size_t slot = 0; slot != slots / 2; slot++)
166 + pgd_append_point(pg, slot, slot, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slot);
167 +
168 + pgdc_reset(&cursor, pg, 0);
169 +
170 + for (size_t slot = 0; slot != slots / 2; slot++) {
171 + EXPECT_TRUE(pgdc_get_next_point(&cursor, slot, &sp));
172 +
173 + EXPECT_EQ(slot, static_cast<size_t>(sp.min));
174 + EXPECT_EQ(sp.min, sp.max);
175 + EXPECT_EQ(sp.min, sp.sum);
176 + EXPECT_EQ(sp.count, 1);
177 + EXPECT_EQ(sp.anomaly_count, 0);
178 + }
179 + EXPECT_FALSE(pgdc_get_next_point(&cursor, slots / 2, &sp));
180 +
181 + // reset pgdc to the end of the page, we should not be getting more
182 + // points even if the page has grown in between.
183 +
184 + pgdc_reset(&cursor, pg, slots / 2);
185 +
186 + for (size_t slot = slots / 2; slot != slots; slot++)
187 + pgd_append_point(pg, slot, slot, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slot);
188 +
189 + for (size_t slot = slots / 2; slot != slots; slot++)
190 + EXPECT_FALSE(pgdc_get_next_point(&cursor, slot, &sp));
191 +
192 + EXPECT_FALSE(pgdc_get_next_point(&cursor, slots, &sp));
193 +
194 + pgd_free(pg);
195 +}
196 +
197 +TEST(PGD, MemoryFootprint) {
198 + size_t slots = slots_for_page(1024 * 1024);
199 + PGD *pg = pgd_create(page_type, slots);
200 +
201 + uint32_t footprint = 0;
202 + switch (pgd_type(pg)) {
203 + case PAGE_METRICS:
204 + footprint = slots * sizeof(uint32_t);
205 + break;
206 + case PAGE_GORILLA_METRICS:
207 + footprint = 128 * sizeof(uint32_t);
208 + break;
209 + default:
210 + fatal("Uknown page type: %uc", pgd_type(pg));
211 + }
212 + EXPECT_NEAR(pgd_memory_footprint(pg), footprint, 128);
213 +
214 + std::random_device rand_dev;
215 + std::mt19937 gen(rand_dev());
216 + std::uniform_int_distribution<uint32_t> distr(std::numeric_limits<uint32_t>::min(),
217 + std::numeric_limits<uint32_t>::max()); // define the range
218 +
219 + for (size_t slot = 0; slot != slots; slot++) {
220 + uint32_t n = distr(gen);
221 + pgd_append_point(pg, slot, n, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slot);
222 + }
223 +
224 + footprint = slots * sizeof(uint32_t);
225 +
226 + uint32_t abs_error = 0;
227 + switch (pgd_type(pg)) {
228 + case PAGE_METRICS:
229 + abs_error = 128;
230 + break;
231 + case PAGE_GORILLA_METRICS:
232 + abs_error = footprint / 10;
233 + break;
234 + default:
235 + fatal("Uknown page type: %uc", pgd_type(pg));
236 + }
237 +
238 + EXPECT_NEAR(pgd_memory_footprint(pg), footprint, abs_error);
239 +}
240 +
241 +TEST(PGD, DiskFootprint) {
242 + size_t slots = slots_for_page(1024 * 1024);
243 + PGD *pg = pgd_create(page_type, slots);
244 +
245 + std::random_device rand_dev;
246 + std::mt19937 gen(rand_dev());
247 + std::uniform_int_distribution<uint32_t> distr(std::numeric_limits<uint32_t>::min(),
248 + std::numeric_limits<uint32_t>::max()); // define the range
249 +
250 + size_t used_slots = 16;
251 +
252 + for (size_t slot = 0; slot != used_slots; slot++) {
253 + uint32_t n = distr(gen);
254 + pgd_append_point(pg, slot, n, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slot);
255 + }
256 +
257 + uint32_t footprint = 0;
258 + switch (pgd_type(pg)) {
259 + case PAGE_METRICS:
260 + footprint = used_slots * sizeof(uint32_t);
261 + break;
262 + case PAGE_GORILLA_METRICS:
263 + footprint = 128 * sizeof(uint32_t);
264 + break;
265 + default:
266 + fatal("Uknown page type: %uc", pgd_type(pg));
267 + }
268 + EXPECT_EQ(pgd_disk_footprint(pg), footprint);
269 +
270 + pgd_free(pg);
271 +
272 + pg = pgd_create(page_type, slots);
273 +
274 + used_slots = 128 + 64;
275 +
276 + for (size_t slot = 0; slot != used_slots; slot++) {
277 + uint32_t n = distr(gen);
278 + pgd_append_point(pg, slot, n, 0, 0, 1, 1, SN_DEFAULT_FLAGS, slot);
279 + }
280 +
281 + switch (pgd_type(pg)) {
282 + case PAGE_METRICS:
283 + footprint = used_slots * sizeof(uint32_t);
284 + break;
285 + case PAGE_GORILLA_METRICS:
286 + footprint = 2 * (128 * sizeof(uint32_t));
287 + break;
288 + default:
289 + fatal("Uknown page type: %uc", pgd_type(pg));
290 + }
291 + EXPECT_EQ(pgd_disk_footprint(pg), footprint);
292 +
293 + pgd_free(pg);
294 +}
295 +
296 +TEST(PGD, CopyToExtent) {
297 + size_t slots = slots_for_page(1024 * 1024);
298 + PGD *pg_collector = pgd_create(page_type, slots);
299 +
300 + uint32_t value = 666;
301 + pgd_append_point(pg_collector, 0, value, 0, 0, 1, 0, SN_DEFAULT_FLAGS, 0);
302 +
303 + uint32_t size_in_bytes = pgd_disk_footprint(pg_collector);
304 + EXPECT_EQ(size_in_bytes, 512);
305 +
306 + uint32_t size_in_words = size_in_bytes / sizeof(uint32_t);
307 + alignas(sizeof(uintptr_t)) uint32_t disk_buffer[size_in_words];
308 +
309 + for (size_t i = 0; i != size_in_words; i++) {
310 + disk_buffer[i] = std::numeric_limits<uint32_t>::max();
311 + }
312 +
313 + pgd_copy_to_extent(pg_collector, (uint8_t *) &disk_buffer[0], size_in_bytes);
314 +
315 + EXPECT_EQ(disk_buffer[0], NULL);
316 + EXPECT_EQ(disk_buffer[1], NULL);
317 + EXPECT_EQ(disk_buffer[2], 1);
318 + EXPECT_EQ(disk_buffer[3], 32);
319 + storage_number sn = pack_storage_number(value, SN_DEFAULT_FLAGS);
320 + EXPECT_EQ(disk_buffer[4], sn);
321 +
322 + // make sure the rest of the page is 0'ed so that it's amenable to compression
323 + for (size_t i = 5; i != size_in_words; i++)
324 + EXPECT_EQ(disk_buffer[i], 0);
325 +
326 + pgd_free(pg_collector);
327 +}
328 +
329 +TEST(PGD, Roundtrip) {
330 + size_t slots = slots_for_page(1024 * 1024);
331 + PGD *pg_collector = pgd_create(page_type, slots);
332 +
333 + for (size_t i = 0; i != slots; i++)
334 + pgd_append_point(pg_collector, i, i, 0, 0, 1, 1, SN_DEFAULT_FLAGS, i);
335 +
336 + uint32_t size_in_bytes = pgd_disk_footprint(pg_collector);
337 + uint32_t size_in_words = size_in_bytes / sizeof(uint32_t);
338 +
339 + alignas(sizeof(uintptr_t)) uint32_t disk_buffer[size_in_words];
340 + for (size_t i = 0; i != size_in_words; i++)
341 + disk_buffer[i] = std::numeric_limits<uint32_t>::max();
342 +
343 + pgd_copy_to_extent(pg_collector, (uint8_t *) &disk_buffer[0], size_in_bytes);
344 +
345 + PGD *pg_disk = pgd_create_from_disk_data(page_type, &disk_buffer[0], size_in_bytes);
346 + EXPECT_EQ(pgd_slots_used(pg_disk), slots);
347 +
348 + // Expected memory footprint is equal to the disk footprint + a couple
349 + // bytes for the PGD metadata.
350 + EXPECT_NEAR(pgd_memory_footprint(pg_disk), size_in_bytes, 128);
351 +
352 + // Do not allow calling disk footprint for pages created from disk.
353 + EXPECT_DEATH(pgd_disk_footprint(pg_disk), ".*");
354 +
355 + for (size_t i = 0; i != 10; i++) {
356 + PGDC cursor_collector;
357 + PGDC cursor_disk;
358 +
359 + pgdc_reset(&cursor_collector, pg_collector, i * 1024);
360 + pgdc_reset(&cursor_disk, pg_disk, i * 1024);
361 +
362 + STORAGE_POINT sp_collector = {};
363 + STORAGE_POINT sp_disk = {};
364 +
365 + for (size_t slot = i * 1024; slot != slots; slot++) {
366 + EXPECT_TRUE(pgdc_get_next_point(&cursor_collector, slot, &sp_collector));
367 + EXPECT_TRUE(pgdc_get_next_point(&cursor_disk, slot, &sp_disk));
368 +
369 + EXPECT_EQ(sp_collector, sp_disk);
370 + }
371 +
372 + EXPECT_FALSE(pgdc_get_next_point(&cursor_collector, slots, &sp_collector));
373 + EXPECT_FALSE(pgdc_get_next_point(&cursor_disk, slots, &sp_disk));
374 + }
375 +
376 + pgd_free(pg_disk);
377 + pgd_free(pg_collector);
378 +}
379 +
380 +int pgd_test(int argc, char *argv[])
381 +{
382 + // Dummy/necessary initialization stuff
383 + PGC *dummy_cache = pgc_create("pgd-tests-cache", 32 * 1024 * 1024, NULL, 64, NULL, NULL,
384 + 10, 10, 1000, 10, PGC_OPTIONS_NONE, 1, 11);
385 + pgd_init_arals();
386 +
387 + ::testing::InitGoogleTest(&argc, argv);
388 + int rc = RUN_ALL_TESTS();
389 +
390 + pgc_destroy(dummy_cache);
391 +
392 + return rc;
393 +}
394 +
395 +#else // HAVE_GTEST
396 +
397 +int pgd_test(int argc, char *argv[])
398 +{
399 + (void) argc;
400 + (void) argv;
401 + fprintf(stderr, "Can not run PGD tests because the agent was not build with support for google tests.\n");
402 + return 0;
403 +}
404 +
405 +#endif // HAVE_GTEST
database/engine/page_test.h new
+14
@@ -0,0 +1,14 @@
1 +#ifndef PAGE_TEST_H
2 +#define PAGE_TEST_H
3 +
4 +#ifdef __cplusplus
5 +extern "C" {
6 +#endif
7 +
8 +int pgd_test(int argc, char *argv[]);
9 +
10 +#ifdef __cplusplus
11 +}
12 +#endif
13 +
14 +#endif /* PAGE_TEST_H */
database/engine/pagecache.c
+15 -47
@@ -12,8 +12,9 @@ struct rrdeng_cache_efficiency_stats rrdeng_cache_efficiency_stats = {};
12 static void main_cache_free_clean_page_callback(PGC *cache __maybe_unused, PGC_ENTRY entry __maybe_unused)
13 {
14 // Release storage associated with the page
15 - dbengine_page_free(entry.data, entry.size);
15 + pgd_free(entry.data);
16 }
17 +
18 static void main_cache_flush_dirty_page_init_callback(PGC *cache __maybe_unused, Word_t section) {
19 struct rrdengine_instance *ctx = (struct rrdengine_instance *) section;
20
@@ -28,8 +29,6 @@ static void main_cache_flush_dirty_page_callback(PGC *cache __maybe_unused, PGC_
29
30 struct rrdengine_instance *ctx = (struct rrdengine_instance *) entries_array[0].section;
31
31 - size_t bytes_per_point = CTX_POINT_SIZE_BYTES(ctx);
32 -
32 struct page_descr_with_data *base = NULL;
33
34 for (size_t Index = 0 ; Index < entries; Index++) {
@@ -42,21 +41,15 @@ static void main_cache_flush_dirty_page_callback(PGC *cache __maybe_unused, PGC_
41 descr->start_time_ut = start_time_s * USEC_PER_SEC;
42 descr->end_time_ut = end_time_s * USEC_PER_SEC;
43 descr->update_every_s = entries_array[Index].update_every_s;
45 - descr->type = ctx->config.page_type;
44
47 - descr->page_length = (end_time_s - (start_time_s - descr->update_every_s)) / descr->update_every_s * bytes_per_point;
45 + descr->pgd = pgc_page_data(pages_array[Index]);
46 + descr->type = pgd_type(descr->pgd);
47 + descr->page_length = pgd_disk_footprint(descr->pgd);
48
49 - if(descr->page_length > entries_array[Index].size) {
50 - descr->page_length = entries_array[Index].size;
51 -
52 - error_limit_static_global_var(erl, 1, 0);
53 - error_limit(&erl, "DBENGINE: page exceeds the maximum size, adjusting it to max.");
54 - }
55 -
56 - descr->page = pgc_page_data(pages_array[Index]);
49 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(base, descr, link.prev, link.next);
50
59 - internal_fatal(descr->page_length > RRDENG_BLOCK_SIZE, "DBENGINE: faulty page length calculation");
51 + // TODO: ask @stelfrag/@ktsaou about this.
52 + // internal_fatal(descr->page_length > RRDENG_BLOCK_SIZE, "DBENGINE: faulty page length calculation");
53 }
54
55 struct completion completion;
@@ -254,7 +247,6 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin
247 time_t page_start_time_s = pgc_page_start_time_s(page);
248 time_t page_end_time_s = pgc_page_end_time_s(page);
249 time_t page_update_every_s = pgc_page_update_every_s(page);
257 - size_t page_length = pgc_page_data_size(cache, page);
250
251 if(!page_update_every_s)
252 page_update_every_s = dt_s;
@@ -277,24 +269,10 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin
269 if (!PValue || PValue == PJERR)
270 fatal("DBENGINE: corrupted judy array in %s()", __FUNCTION__ );
271
280 - if (unlikely(*PValue)) {
281 - struct page_details *pd = *PValue;
282 - UNUSED(pd);
283 -
284 -// internal_error(
285 -// pd->first_time_s != page_first_time_s ||
286 -// pd->last_time_s != page_last_time_s ||
287 -// pd->update_every_s != page_update_every_s,
288 -// "DBENGINE: duplicate page with different retention in %s cache "
289 -// "1st: %ld to %ld, ue %u, size %u "
290 -// "2nd: %ld to %ld, ue %ld size %zu "
291 -// "- ignoring the second",
292 -// cache == open_cache ? "open" : "main",
293 -// pd->first_time_s, pd->last_time_s, pd->update_every_s, pd->page_length,
294 -// page_first_time_s, page_last_time_s, page_update_every_s, page_length);
295 -
272 + if (unlikely(*PValue))
273 + // already exists in our list
274 pgc_page_release(cache, page);
297 - }
275 +
276 else {
277
278 internal_fatal(pgc_page_metric(page) != metric_id, "Wrong metric id in page found in cache");
@@ -304,7 +282,6 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin
282 pd->metric_id = metric_id;
283 pd->first_time_s = page_start_time_s;
284 pd->last_time_s = page_end_time_s;
307 - pd->page_length = page_length;
285 pd->update_every_s = (uint32_t) page_update_every_s;
286 pd->page = (open_cache_mode) ? NULL : page;
287 pd->status |= tags;
@@ -312,7 +289,7 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin
289 if((pd->page)) {
290 pd->status |= PDC_PAGE_READY | PDC_PAGE_PRELOADED;
291
315 - if(pgc_page_data(page) == DBENGINE_EMPTY_PAGE)
292 + if(pgd_is_empty(pgc_page_data(page)))
293 pd->status |= PDC_PAGE_EMPTY;
294 }
295
@@ -369,7 +346,7 @@ static void pgc_inject_gap(struct rrdengine_instance *ctx, METRIC *metric, time_
346 .end_time_s = MIN(end_time_s, db_last_time_s),
347 .update_every_s = 0,
348 .size = 0,
372 - .data = DBENGINE_EMPTY_PAGE,
349 + .data = PGD_EMPTY,
350 };
351
352 if(page_entry.start_time_s >= page_entry.end_time_s)
@@ -478,7 +455,7 @@ static size_t list_has_time_gaps(
455 pd->status &= ~PDC_PAGE_DISK_PENDING;
456 pd->status |= PDC_PAGE_READY | PDC_PAGE_PRELOADED | PDC_PAGE_PRELOADED_PASS4;
457
481 - if(pgc_page_data(pd->page) == DBENGINE_EMPTY_PAGE)
458 + if(pgd_is_empty(pgc_page_data(pd->page)))
459 pd->status |= PDC_PAGE_EMPTY;
460
461 }
@@ -642,7 +619,6 @@ void add_page_details_from_journal_v2(PGC_PAGE *page, void *JudyL_pptr) {
619 pd->first_time_s = pgc_page_start_time_s(page);
620 pd->last_time_s = pgc_page_end_time_s(page);
621 pd->datafile.ptr = datafile;
645 - pd->page_length = ei->page_length;
622 pd->update_every_s = (uint32_t) pgc_page_update_every_s(page);
623 pd->metric_id = metric_id;
624 pd->status |= PDC_PAGE_DISK_PENDING | PDC_PAGE_SOURCE_JOURNAL_V2 | PDC_PAGE_DATAFILE_ACQUIRED;
@@ -917,7 +893,7 @@ struct pgc_page *pg_cache_lookup_next(
893 }
894 }
895
920 - if(page && pgc_page_data(page) == DBENGINE_EMPTY_PAGE)
896 + if(page && pgd_is_empty(pgc_page_data(page)))
897 pdc_page_status_set(pd, PDC_PAGE_EMPTY);
898
899 if(!page || pdc_page_status_check(pd, PDC_PAGE_QUERY_GLOBAL_SKIP_LIST | PDC_PAGE_EMPTY)) {
@@ -930,7 +906,6 @@ struct pgc_page *pg_cache_lookup_next(
906 time_t page_start_time_s = pgc_page_start_time_s(page);
907 time_t page_end_time_s = pgc_page_end_time_s(page);
908 time_t page_update_every_s = pgc_page_update_every_s(page);
933 - size_t page_length = pgc_page_data_size(main_cache, page);
909
910 if(unlikely(page_start_time_s == INVALID_TIME || page_end_time_s == INVALID_TIME)) {
911 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_zero_time_skipped, 1, __ATOMIC_RELAXED);
@@ -939,13 +914,6 @@ struct pgc_page *pg_cache_lookup_next(
914 pd->page = page = NULL;
915 continue;
916 }
942 - else if(page_length > RRDENG_BLOCK_SIZE) {
943 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_invalid_size_skipped, 1, __ATOMIC_RELAXED);
944 - pgc_page_to_clean_evict_or_release(main_cache, page);
945 - pdc_page_status_set(pd, PDC_PAGE_INVALID | PDC_PAGE_RELEASED);
946 - pd->page = page = NULL;
947 - continue;
948 - }
917 else {
918 if (unlikely(page_update_every_s <= 0 || page_update_every_s > 86400)) {
919 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_invalid_update_every_fixed, 1, __ATOMIC_RELAXED);
@@ -953,7 +921,7 @@ struct pgc_page *pg_cache_lookup_next(
921 pd->update_every_s = (uint32_t) page_update_every_s;
922 }
923
956 - size_t entries_by_size = page_entries_by_size(page_length, CTX_POINT_SIZE_BYTES(ctx));
924 + size_t entries_by_size = pgd_slots_used(pgc_page_data(page));
925 size_t entries_by_time = page_entries_by_time(page_start_time_s, page_end_time_s, page_update_every_s);
926 if(unlikely(entries_by_size < entries_by_time)) {
927 time_t fixed_page_end_time_s = (time_t)(page_start_time_s + (entries_by_size - 1) * page_update_every_s);
database/engine/pagecache.h
+1 -1
@@ -27,7 +27,7 @@ struct page_descr_with_data {
27 uint8_t type;
28 uint32_t update_every_s;
29 uint32_t page_length;
30 - uint8_t *page;
30 + struct pgd *pgd;
31
32 struct {
33 struct page_descr_with_data *prev;
database/engine/pdc.c
+73 -26
@@ -629,14 +629,33 @@ void collect_page_flags_to_buffer(BUFFER *wb, RRDENG_COLLECT_PAGE_FLAGS flags) {
629 }
630
631 inline VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_extent_page_descr *descr, time_t now_s, time_t overwrite_zero_update_every_s, bool have_read_error) {
632 + time_t start_time_s = (time_t) (descr->start_time_ut / USEC_PER_SEC);
633 +
634 + time_t end_time_s;
635 + size_t entries;
636 +
637 + switch (descr->type) {
638 + case PAGE_METRICS:
639 + case PAGE_TIER:
640 + end_time_s = descr->end_time_ut / USEC_PER_SEC;
641 + entries = 0;
642 + break;
643 + case PAGE_GORILLA_METRICS:
644 + end_time_s = start_time_s + descr->gorilla.delta_time_s;
645 + entries = descr->gorilla.entries;
646 + break;
647 + default:
648 + fatal("Unknown page type: %uc\n", descr->type);
649 + }
650 +
651 return validate_page(
652 (uuid_t *)descr->uuid,
634 - (time_t) (descr->start_time_ut / USEC_PER_SEC),
635 - (time_t) (descr->end_time_ut / USEC_PER_SEC),
653 + start_time_s,
654 + end_time_s,
655 0,
656 descr->page_length,
657 descr->type,
639 - 0,
658 + entries,
659 now_s,
660 overwrite_zero_update_every_s,
661 have_read_error,
@@ -666,13 +685,25 @@ VALIDATED_PAGE_DESCRIPTOR validate_page(
685 .is_valid = true,
686 };
687
669 - // always calculate entries by size
688 vd.point_size = page_type_size[vd.type];
671 - vd.entries = page_entries_by_size(vd.page_length, vd.point_size);
672 -
673 - // allow to be called without entries (when loading pages from disk)
674 - if(!entries)
675 - entries = vd.entries;
689 + switch (page_type) {
690 + case PAGE_METRICS:
691 + case PAGE_TIER:
692 + // always calculate entries by size
693 + vd.entries = page_entries_by_size(vd.page_length, vd.point_size);
694 +
695 + // allow to be called without entries (when loading pages from disk)
696 + if(!entries)
697 + entries = vd.entries;
698 + break;
699 + case PAGE_GORILLA_METRICS:
700 + internal_fatal(entries == 0, "0 number of entries found on gorilla page");
701 + vd.entries = entries;
702 + break;
703 + default:
704 + // TODO: should set vd.is_valid false instead?
705 + fatal("Unknown page type: %uc", page_type);
706 + }
707
708 // allow to be called without update every (when loading pages from disk)
709 if(!update_every_s) {
@@ -687,19 +718,26 @@ VALIDATED_PAGE_DESCRIPTOR validate_page(
718
719 bool updated = false;
720
721 + size_t max_page_length = RRDENG_BLOCK_SIZE;
722 +
723 + // If gorilla can not compress the data we might end up needing slightly more
724 + // than 4KiB. However, gorilla pages extend the page length by increments of
725 + // 512 bytes.
726 + max_page_length += ((page_type == PAGE_GORILLA_METRICS) * GORILLA_BUFFER_SIZE);
727 +
728 if( have_read_error ||
729 vd.page_length == 0 ||
692 - vd.page_length > RRDENG_BLOCK_SIZE ||
730 + vd.page_length > max_page_length ||
731 vd.start_time_s > vd.end_time_s ||
732 (now_s && vd.end_time_s > now_s) ||
733 vd.start_time_s <= 0 ||
734 vd.end_time_s <= 0 ||
735 vd.update_every_s < 0 ||
736 (vd.start_time_s == vd.end_time_s && vd.entries > 1) ||
699 - (vd.update_every_s == 0 && vd.entries > 1)
700 - )
737 + (vd.update_every_s == 0 && vd.entries > 1))
738 + {
739 vd.is_valid = false;
702 -
740 + }
741 else {
742 if(unlikely(vd.entries != entries || vd.update_every_s != update_every_s))
743 updated = true;
@@ -832,7 +870,15 @@ static void epdl_extent_loading_error_log(struct rrdengine_instance *ctx, EPDL *
870
871 if (descr) {
872 start_time_s = (time_t)(descr->start_time_ut / USEC_PER_SEC);
835 - end_time_s = (time_t)(descr->end_time_ut / USEC_PER_SEC);
873 + switch (descr->type) {
874 + case PAGE_METRICS:
875 + case PAGE_TIER:
876 + end_time_s = (time_t)(descr->end_time_ut / USEC_PER_SEC);
877 + break;
878 + case PAGE_GORILLA_METRICS:
879 + end_time_s = (time_t) start_time_s + (descr->gorilla.delta_time_s);
880 + break;
881 + }
882 uuid_unparse_lower(descr->uuid, uuid);
883 used_descr = true;
884 }
@@ -1020,16 +1066,17 @@ static bool epdl_populate_pages_from_extent_data(
1066 if(worker)
1067 worker_is_busy(UV_EVENT_DBENGINE_EXTENT_PAGE_ALLOCATION);
1068
1023 - void *page_data;
1069 + PGD *pgd;
1070
1071 if (unlikely(!vd.is_valid)) {
1026 - page_data = DBENGINE_EMPTY_PAGE;
1072 + pgd = PGD_EMPTY;
1073 stats_load_invalid_page++;
1074 }
1075 else {
1076 if (RRD_NO_COMPRESSION == header->compression_algorithm) {
1031 - page_data = dbengine_page_alloc(vd.page_length);
1032 - memcpy(page_data, data + payload_offset + page_offset, (size_t) vd.page_length);
1077 + pgd = pgd_create_from_disk_data(header->descr[i].type,
1078 + data + payload_offset + page_offset,
1079 + vd.page_length);
1080 stats_load_uncompressed++;
1081 }
1082 else {
@@ -1040,12 +1087,13 @@ static bool epdl_populate_pages_from_extent_data(
1087 i, count, page_offset, vd.page_length, uncompressed_payload_length);
1088 epdl_extent_loading_error_log(ctx, epdl, &header->descr[i], log);
1089
1043 - page_data = DBENGINE_EMPTY_PAGE;
1090 + pgd = PGD_EMPTY;
1091 stats_load_invalid_page++;
1092 }
1093 else {
1047 - page_data = dbengine_page_alloc(vd.page_length);
1048 - memcpy(page_data, uncompressed_buf + page_offset, vd.page_length);
1094 + pgd = pgd_create_from_disk_data(header->descr[i].type,
1095 + uncompressed_buf + page_offset,
1096 + vd.page_length);
1097 stats_load_compressed++;
1098 }
1099 }
@@ -1061,14 +1109,14 @@ static bool epdl_populate_pages_from_extent_data(
1109 .start_time_s = vd.start_time_s,
1110 .end_time_s = vd.end_time_s,
1111 .update_every_s = (uint32_t) vd.update_every_s,
1064 - .size = (size_t) ((page_data == DBENGINE_EMPTY_PAGE) ? 0 : vd.page_length),
1065 - .data = page_data
1112 + .size = pgd_memory_footprint(pgd), // the footprint of the entire PGD, for accurate memory management
1113 + .data = pgd,
1114 };
1115
1116 bool added = true;
1117 PGC_PAGE *page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
1118 if (false == added) {
1071 - dbengine_page_free(page_data, vd.page_length);
1119 + pgd_free(pgd);
1120 stats_cache_hit_while_inserting++;
1121 stats_data_from_main_cache++;
1122 }
@@ -1081,8 +1129,7 @@ static bool epdl_populate_pages_from_extent_data(
1129 pgc_page_dup(main_cache, page);
1130
1131 pd->page = page;
1084 - pd->page_length = pgc_page_data_size(main_cache, page);
1085 - pdc_page_status_set(pd, PDC_PAGE_READY | tags | ((page_data == DBENGINE_EMPTY_PAGE) ? PDC_PAGE_EMPTY : 0));
1132 + pdc_page_status_set(pd, PDC_PAGE_READY | tags | (pgd_is_empty(pgd) ? PDC_PAGE_EMPTY : 0));
1133
1134 pd = pd->load.next;
1135 } while(pd);
database/engine/rrddiskprotocol.h
+12 -2
@@ -3,6 +3,8 @@
3 #ifndef NETDATA_RRDDISKPROTOCOL_H
4 #define NETDATA_RRDDISKPROTOCOL_H
5
6 +#include <stdint.h>
7 +
8 #define RRDENG_BLOCK_SIZE (4096)
9 #define RRDFILE_ALIGNMENT RRDENG_BLOCK_SIZE
10
@@ -36,7 +38,8 @@ struct rrdeng_df_sb {
38 */
39 #define PAGE_METRICS (0)
40 #define PAGE_TIER (1)
39 -#define PAGE_TYPE_MAX 1 // Maximum page type (inclusive)
41 +#define PAGE_GORILLA_METRICS (2)
42 +#define PAGE_TYPE_MAX 2 // Maximum page type (inclusive)
43
44 /*
45 * Data file page descriptor
@@ -47,7 +50,14 @@ struct rrdeng_extent_page_descr {
50 uint8_t uuid[UUID_SZ];
51 uint32_t page_length;
52 uint64_t start_time_ut;
50 - uint64_t end_time_ut;
53 + union {
54 + struct {
55 + uint32_t entries;
56 + uint32_t delta_time_s;
57 + } gorilla __attribute__((packed));
58 +
59 + uint64_t end_time_ut;
60 + };
61 } __attribute__ ((packed));
62
63 /*
database/engine/rrdengine.c
+16 -52
@@ -575,55 +575,6 @@ static inline struct rrdeng_cmd rrdeng_deq_cmd(bool from_worker) {
575 }
576
577
578 -// ----------------------------------------------------------------------------
579 -
580 -struct {
581 - ARAL *aral[RRD_STORAGE_TIERS];
582 -} dbengine_page_alloc_globals = {};
583 -
584 -static inline ARAL *page_size_lookup(size_t size) {
585 - for(size_t tier = 0; tier < storage_tiers ;tier++)
586 - if(size == tier_page_size[tier])
587 - return dbengine_page_alloc_globals.aral[tier];
588 -
589 - return NULL;
590 -}
591 -
592 -static void dbengine_page_alloc_init(void) {
593 - for(size_t i = storage_tiers; i > 0 ;i--) {
594 - size_t tier = storage_tiers - i;
595 -
596 - char buf[20 + 1];
597 - snprintfz(buf, 20, "tier%zu-pages", tier);
598 -
599 - dbengine_page_alloc_globals.aral[tier] = aral_create(
600 - buf,
601 - tier_page_size[tier],
602 - 64,
603 - 512 * tier_page_size[tier],
604 - pgc_aral_statistics(),
605 - NULL, NULL, false, false);
606 - }
607 -}
608 -
609 -void *dbengine_page_alloc(size_t size) {
610 - ARAL *ar = page_size_lookup(size);
611 - if(ar) return aral_mallocz(ar);
612 -
613 - return mallocz(size);
614 -}
615 -
616 -void dbengine_page_free(void *page, size_t size __maybe_unused) {
617 - if(unlikely(!page || page == DBENGINE_EMPTY_PAGE))
618 - return;
619 -
620 - ARAL *ar = page_size_lookup(size);
621 - if(ar)
622 - aral_freez(ar, page);
623 - else
624 - freez(page);
625 -}
626 -
578 // ----------------------------------------------------------------------------
579
580 void *dbengine_extent_alloc(size_t size) {
@@ -890,12 +841,25 @@ static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_insta
841 uuid_copy(*(uuid_t *)header->descr[i].uuid, *descr->id);
842 header->descr[i].page_length = descr->page_length;
843 header->descr[i].start_time_ut = descr->start_time_ut;
893 - header->descr[i].end_time_ut = descr->end_time_ut;
844 +
845 + switch (descr->type) {
846 + case PAGE_METRICS:
847 + case PAGE_TIER:
848 + header->descr[i].end_time_ut = descr->end_time_ut;
849 + break;
850 + case PAGE_GORILLA_METRICS:
851 + header->descr[i].gorilla.delta_time_s = (uint32_t) ((descr->end_time_ut - descr->start_time_ut) / USEC_PER_SEC);
852 + header->descr[i].gorilla.entries = pgd_slots_used(descr->pgd);
853 + break;
854 + default:
855 + fatal("Unknown page type: %uc", descr->type);
856 + }
857 +
858 pos += sizeof(header->descr[i]);
859 }
860 for (i = 0 ; i < count ; ++i) {
861 descr = xt_io_descr->descr_array[i];
898 - (void) memcpy(xt_io_descr->buf + pos, descr->page, descr->page_length);
862 + pgd_copy_to_extent(descr->pgd, xt_io_descr->buf + pos, descr->page_length);
863 pos += descr->page_length;
864 }
865
@@ -1628,7 +1592,7 @@ static void dbengine_initialize_structures(void) {
1592 rrdeng_query_handle_init();
1593 page_descriptors_init();
1594 extent_buffer_init();
1631 - dbengine_page_alloc_init();
1595 + pgd_init_arals();
1596 extent_io_descriptor_init();
1597 }
1598
database/engine/rrdengine.h
+5 -10
@@ -22,6 +22,7 @@
22 #include "metric.h"
23 #include "cache.h"
24 #include "pdc.h"
25 +#include "page.h"
26
27 extern unsigned rrdeng_pages_per_extent;
28
@@ -119,7 +120,6 @@ struct page_details {
120 time_t first_time_s;
121 time_t last_time_s;
122 uint32_t update_every_s;
122 - uint16_t page_length;
123 PDC_PAGE_STATUS status;
124
125 struct {
@@ -192,9 +192,9 @@ struct rrdeng_collect_handle {
192
193 struct rrdengine_instance *ctx;
194 struct metric *metric;
195 - struct pgc_page *page;
196 - void *data;
197 - size_t data_size;
195 + struct pgc_page *pgc_page;
196 + struct pgd *page_data;
197 + size_t page_data_size;
198 struct pg_alignment *alignment;
199 uint32_t page_entries_max;
200 uint32_t page_position; // keep track of the current page size, to make sure we don't exceed it
@@ -207,7 +207,7 @@ struct rrdeng_query_handle {
207 struct metric *metric;
208 struct pgc_page *page;
209 struct rrdengine_instance *ctx;
210 - storage_number *metric_data;
210 + struct pgd_cursor pgdc;
211 struct page_details_control *pdc;
212
213 // the request
@@ -446,9 +446,6 @@ static inline void ctx_last_flush_fileno_set(struct rrdengine_instance *ctx, uns
446
447 #define ctx_is_available_for_queries(ctx) (__atomic_load_n(&(ctx)->quiesce.enabled, __ATOMIC_RELAXED) == false && __atomic_load_n(&(ctx)->quiesce.exit_mode, __ATOMIC_RELAXED) == false)
448
449 -void *dbengine_page_alloc(size_t size);
450 -void dbengine_page_free(void *page, size_t size);
451 -
449 void *dbengine_extent_alloc(size_t size);
450 void dbengine_extent_free(void *extent, size_t size);
451
@@ -492,8 +489,6 @@ typedef struct validated_page_descriptor {
489 bool is_valid;
490 } VALIDATED_PAGE_DESCRIPTOR;
491
495 -#define DBENGINE_EMPTY_PAGE (void *)(-1)
496 -
492 #define page_entries_by_time(start_time_s, end_time_s, update_every_s) \
493 ((update_every_s) ? (((end_time_s) - ((start_time_s) - (update_every_s))) / (update_every_s)) : 1)
494
database/engine/rrdengineapi.c
+85 -137
@@ -1,4 +1,6 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "database/engine/rrddiskprotocol.h"
4 #include "rrdengine.h"
5
6 /* Default global database instance */
@@ -22,10 +24,15 @@ size_t tier_page_size[RRD_STORAGE_TIERS] = {2048, 1024, 192, 192, 192};
24 size_t tier_page_size[RRD_STORAGE_TIERS] = {4096, 2048, 384, 384, 384};
25 #endif
26
25 -#if PAGE_TYPE_MAX != 1
26 -#error PAGE_TYPE_MAX is not 1 - you need to add allocations here
27 +#if PAGE_TYPE_MAX != 2
28 +#error PAGE_TYPE_MAX is not 2 - you need to add allocations here
29 #endif
28 -size_t page_type_size[256] = {sizeof(storage_number), sizeof(storage_number_tier1_t)};
30 +
31 +size_t page_type_size[256] = {
32 + [PAGE_METRICS] = sizeof(storage_number),
33 + [PAGE_TIER] = sizeof(storage_number_tier1_t),
34 + [PAGE_GORILLA_METRICS] = sizeof(storage_number)
35 +};
36
37 __attribute__((constructor)) void initialize_multidb_ctx(void) {
38 multidb_ctx[0] = &multidb_ctx_storage_tier0;
@@ -198,15 +205,15 @@ static inline void check_and_fix_mrg_update_every(struct rrdeng_collect_handle *
205
206 static inline bool check_completed_page_consistency(struct rrdeng_collect_handle *handle __maybe_unused) {
207 #ifdef NETDATA_INTERNAL_CHECKS
201 - if (unlikely(!handle->page || !handle->page_entries_max || !handle->page_position || !handle->page_end_time_ut))
208 + if (unlikely(!handle->pgc_page || !handle->page_entries_max || !handle->page_position || !handle->page_end_time_ut))
209 return false;
210
204 - struct rrdengine_instance *ctx = handle->ctx;
211 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
212
213 uuid_t *uuid = mrg_metric_uuid(main_mrg, handle->metric);
207 - time_t start_time_s = pgc_page_start_time_s(handle->page);
208 - time_t end_time_s = pgc_page_end_time_s(handle->page);
209 - time_t update_every_s = pgc_page_update_every_s(handle->page);
214 + time_t start_time_s = pgc_page_start_time_s(handle->pgc_page);
215 + time_t end_time_s = pgc_page_end_time_s(handle->pgc_page);
216 + time_t update_every_s = pgc_page_update_every_s(handle->pgc_page);
217 size_t page_length = handle->page_position * CTX_POINT_SIZE_BYTES(ctx);
218 size_t entries = handle->page_position;
219 time_t overwrite_zero_update_every_s = (time_t)(handle->update_every_ut / USEC_PER_SEC);
@@ -257,10 +264,11 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metri
264 handle = callocz(1, sizeof(struct rrdeng_collect_handle));
265 handle->common.backend = STORAGE_ENGINE_BACKEND_DBENGINE;
266 handle->metric = metric;
260 - handle->ctx = ctx;
261 - handle->page = NULL;
262 - handle->data = NULL;
263 - handle->data_size = 0;
267 +
268 + handle->pgc_page = NULL;
269 + handle->page_data = NULL;
270 + handle->page_data_size = 0;
271 +
272 handle->page_position = 0;
273 handle->page_entries_max = 0;
274 handle->update_every_ut = (usec_t)update_every * USEC_PER_SEC;
@@ -287,65 +295,29 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metri
295 return (STORAGE_COLLECT_HANDLE *)handle;
296 }
297
290 -/* The page must be populated and referenced */
291 -static bool page_has_only_empty_metrics(struct rrdeng_collect_handle *handle) {
292 - switch(handle->type) {
293 - case PAGE_METRICS: {
294 - size_t slots = handle->page_position;
295 - storage_number *array = (storage_number *)pgc_page_data(handle->page);
296 - for (size_t i = 0 ; i < slots; ++i) {
297 - if(does_storage_number_exist(array[i]))
298 - return false;
299 - }
300 - }
301 - break;
302 -
303 - case PAGE_TIER: {
304 - size_t slots = handle->page_position;
305 - storage_number_tier1_t *array = (storage_number_tier1_t *)pgc_page_data(handle->page);
306 - for (size_t i = 0 ; i < slots; ++i) {
307 - if(fpclassify(array[i].sum_value) != FP_NAN)
308 - return false;
309 - }
310 - }
311 - break;
312 -
313 - default: {
314 - static bool logged = false;
315 - if(!logged) {
316 - netdata_log_error("DBENGINE: cannot check page for nulls on unknown page type id %d", handle->ctx->config.page_type);
317 - logged = true;
318 - }
319 - return false;
320 - }
321 - }
322 -
323 - return true;
324 -}
325 -
298 void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle) {
299 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
300
329 - if (unlikely(!handle->page))
301 + if (unlikely(!handle->pgc_page))
302 return;
303
332 - if(!handle->page_position || page_has_only_empty_metrics(handle))
333 - pgc_page_to_clean_evict_or_release(main_cache, handle->page);
304 + if(pgd_is_empty(handle->page_data))
305 + pgc_page_to_clean_evict_or_release(main_cache, handle->pgc_page);
306
307 else {
308 check_completed_page_consistency(handle);
337 - mrg_metric_set_clean_latest_time_s(main_mrg, handle->metric, pgc_page_end_time_s(handle->page));
338 - pgc_page_hot_to_dirty_and_release(main_cache, handle->page);
309 + mrg_metric_set_clean_latest_time_s(main_mrg, handle->metric, pgc_page_end_time_s(handle->pgc_page));
310 + pgc_page_hot_to_dirty_and_release(main_cache, handle->pgc_page);
311 }
312
313 mrg_metric_set_hot_latest_time_s(main_mrg, handle->metric, 0);
314
343 - handle->page = NULL;
315 + handle->pgc_page = NULL;
316 handle->page_flags = 0;
317 handle->page_position = 0;
318 handle->page_entries_max = 0;
347 - handle->data = NULL;
348 - handle->data_size = 0;
319 + handle->page_data = NULL;
320 + handle->page_data_size = 0;
321
322 // important!
323 // we should never zero page end time ut, because this will allow
@@ -359,10 +331,10 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h
331 }
332
333 static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *handle,
362 - struct rrdengine_instance *ctx,
363 - usec_t point_in_time_ut,
364 - void *data,
365 - size_t data_size) {
334 + struct rrdengine_instance *ctx,
335 + usec_t point_in_time_ut,
336 + PGD *data,
337 + size_t data_size) {
338 time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
339 const time_t update_every_s = (time_t)(handle->update_every_ut / USEC_PER_SEC);
340
@@ -379,7 +351,7 @@ static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *ha
351
352 size_t conflicts = 0;
353 bool added = true;
382 - PGC_PAGE *page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
354 + PGC_PAGE *pgc_page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
355 while (unlikely(!added)) {
356 conflicts++;
357
@@ -391,31 +363,31 @@ static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *ha
363 #else
364 error_limit_static_global_var(erl, 1, 0);
365 error_limit(&erl,
394 -#endif
366 + #endif
367 "DBENGINE: metric '%s' new page from %ld to %ld, update every %ld, has a conflict in main cache "
368 "with existing %s%s page from %ld to %ld, update every %ld - "
369 "is it collected more than once?",
398 - uuid,
399 - page_entry.start_time_s, page_entry.end_time_s, (time_t)page_entry.update_every_s,
400 - pgc_is_page_hot(page) ? "hot" : "not-hot",
401 - pgc_page_data(page) == DBENGINE_EMPTY_PAGE ? " gap" : "",
402 - pgc_page_start_time_s(page), pgc_page_end_time_s(page), pgc_page_update_every_s(page)
370 + uuid,
371 + page_entry.start_time_s, page_entry.end_time_s, (time_t)page_entry.update_every_s,
372 + pgc_is_page_hot(pgc_page) ? "hot" : "not-hot",
373 + pgc_page_data(pgc_page) == PGD_EMPTY ? " gap" : "",
374 + pgc_page_start_time_s(pgc_page), pgc_page_end_time_s(pgc_page), pgc_page_update_every_s(pgc_page)
375 );
376
405 - pgc_page_release(main_cache, page);
377 + pgc_page_release(main_cache, pgc_page);
378
379 point_in_time_ut -= handle->update_every_ut;
380 point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
381 page_entry.start_time_s = point_in_time_s;
382 page_entry.end_time_s = point_in_time_s;
411 - page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
383 + pgc_page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
384 }
385
386 handle->page_entries_max = data_size / CTX_POINT_SIZE_BYTES(ctx);
387 handle->page_start_time_ut = point_in_time_ut;
388 handle->page_end_time_ut = point_in_time_ut;
389 handle->page_position = 1; // zero is already in our data
418 - handle->page = page;
390 + handle->pgc_page = pgc_page;
391 handle->page_flags = conflicts? RRDENG_PAGE_CONFLICT : 0;
392
393 if(point_in_time_s > max_acceptable_collected_time())
@@ -442,9 +414,11 @@ static size_t aligned_allocation_entries(size_t max_slots, size_t target_slot, t
414 return slots;
415 }
416
445 -static void *rrdeng_alloc_new_metric_data(struct rrdeng_collect_handle *handle, size_t *data_size, usec_t point_in_time_ut) {
446 - struct rrdengine_instance *ctx = handle->ctx;
417 +static PGD *rrdeng_alloc_new_page_data(struct rrdeng_collect_handle *handle, size_t *data_size, usec_t point_in_time_ut) {
418 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
419
420 + PGD *d = NULL;
421 +
422 size_t max_size = tier_page_size[ctx->config.tier];
423 size_t max_slots = max_size / CTX_POINT_SIZE_BYTES(ctx);
424
@@ -468,10 +442,22 @@ static void *rrdeng_alloc_new_metric_data(struct rrdeng_collect_handle *handle,
442 internal_fatal(size > tier_page_size[ctx->config.tier] || size < CTX_POINT_SIZE_BYTES(ctx) * 2, "ooops! wrong page size");
443
444 *data_size = size;
471 - void *d = dbengine_page_alloc(size);
445
473 - timing_step(TIMING_STEP_DBENGINE_PAGE_ALLOC);
446 + switch (ctx->config.page_type) {
447 + case PAGE_METRICS:
448 + case PAGE_TIER:
449 + d = pgd_create(ctx->config.page_type, slots);
450 + break;
451 + case PAGE_GORILLA_METRICS:
452 + // ignore slots, and use the fixed number of slots per gorilla buffer.
453 + // gorilla will automatically add more buffers if needed.
454 + d = pgd_create(ctx->config.page_type, GORILLA_BUFFER_SLOTS);
455 + break;
456 + default:
457 + fatal("Unknown page type: %uc\n", ctx->config.page_type);
458 + }
459
460 + timing_step(TIMING_STEP_DBENGINE_PAGE_ALLOC);
461 return d;
462 }
463
@@ -485,39 +471,27 @@ static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_
471 const SN_FLAGS flags)
472 {
473 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
488 - struct rrdengine_instance *ctx = handle->ctx;
474 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
475
490 - if(unlikely(!handle->data))
491 - handle->data = rrdeng_alloc_new_metric_data(handle, &handle->data_size, point_in_time_ut);
476 + if(unlikely(!handle->page_data))
477 + handle->page_data = rrdeng_alloc_new_page_data(handle, &handle->page_data_size, point_in_time_ut);
478
479 timing_step(TIMING_STEP_DBENGINE_CHECK_DATA);
480
495 - if(likely(ctx->config.page_type == PAGE_METRICS)) {
496 - storage_number *tier0_metric_data = handle->data;
497 - tier0_metric_data[handle->page_position] = pack_storage_number(n, flags);
498 - }
499 - else if(likely(ctx->config.page_type == PAGE_TIER)) {
500 - storage_number_tier1_t *tier12_metric_data = handle->data;
501 - storage_number_tier1_t number_tier1;
502 - number_tier1.sum_value = (float) n;
503 - number_tier1.min_value = (float) min_value;
504 - number_tier1.max_value = (float) max_value;
505 - number_tier1.anomaly_count = anomaly_count;
506 - number_tier1.count = count;
507 - tier12_metric_data[handle->page_position] = number_tier1;
508 - }
509 - else
510 - fatal("DBENGINE: cannot store metric on unknown page type id %d", ctx->config.page_type);
481 + pgd_append_point(handle->page_data,
482 + point_in_time_ut,
483 + n, min_value, max_value, count, anomaly_count, flags,
484 + handle->page_position);
485
486 timing_step(TIMING_STEP_DBENGINE_PACK);
487
514 - if(unlikely(!handle->page)){
515 - rrdeng_store_metric_create_new_page(handle, ctx, point_in_time_ut, handle->data, handle->data_size);
488 + if(unlikely(!handle->pgc_page)) {
489 + rrdeng_store_metric_create_new_page(handle, ctx, point_in_time_ut, handle->page_data, handle->page_data_size);
490 // handle->position is set to 1 already
491 }
492 else {
493 // update an existing page
520 - pgc_page_hot_set_end_time_s(main_cache, handle->page, (time_t) (point_in_time_ut / USEC_PER_SEC));
494 + pgc_page_hot_set_end_time_s(main_cache, handle->pgc_page, (time_t) (point_in_time_ut / USEC_PER_SEC));
495 handle->page_end_time_ut = point_in_time_ut;
496
497 if(unlikely(++handle->page_position >= handle->page_entries_max)) {
@@ -542,7 +516,7 @@ static void store_metric_next_error_log(struct rrdeng_collect_handle *handle __m
516 uuid_unparse(*mrg_metric_uuid(main_mrg, handle->metric), uuid);
517
518 BUFFER *wb = NULL;
545 - if(handle->page && handle->page_flags) {
519 + if(handle->pgc_page && handle->page_flags) {
520 wb = buffer_create(0, NULL);
521 collect_page_flags_to_buffer(wb, handle->page_flags);
522 }
@@ -556,7 +530,7 @@ static void store_metric_next_error_log(struct rrdeng_collect_handle *handle __m
530 msg,
531 (time_t)(handle->page_end_time_ut / USEC_PER_SEC),
532 (time_t)(handle->update_every_ut / USEC_PER_SEC),
559 - handle->page ? "current" : "*LAST*",
533 + handle->pgc_page ? "current" : "*LAST*",
534 (time_t)(handle->page_start_time_ut / USEC_PER_SEC),
535 (time_t)(handle->page_end_time_ut / USEC_PER_SEC),
536 handle->page_position, handle->page_entries_max,
@@ -594,7 +568,7 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
568 ;
569 }
570 else if(unlikely(point_in_time_ut > handle->page_end_time_ut)) {
597 - if(handle->page) {
571 + if(handle->pgc_page) {
572 if (unlikely(delta_ut < handle->update_every_ut)) {
573 handle->page_flags |= RRDENG_PAGE_STEP_TOO_SMALL;
574 rrdeng_store_metric_flush_current_page(collection_handle);
@@ -657,7 +631,7 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
631 */
632 int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) {
633 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
660 - struct rrdengine_instance *ctx = handle->ctx;
634 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
635
636 handle->page_flags |= RRDENG_PAGE_COLLECT_FINALIZE;
637 rrdeng_store_metric_flush_current_page(collection_handle);
@@ -802,12 +776,13 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle,
776
777 static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_handle, bool debug_this __maybe_unused) {
778 struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle;
805 - struct rrdengine_instance *ctx = handle->ctx;
779 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
780
781 if (likely(handle->page)) {
782 // we have a page to release
783 pgc_page_release(main_cache, handle->page);
784 handle->page = NULL;
785 + pgdc_reset(&handle->pgdc, NULL, UINT32_MAX);
786 }
787
788 if (unlikely(handle->now_s > rrddim_handle->end_time_s))
@@ -816,10 +791,10 @@ static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_han
791 size_t entries = 0;
792 handle->page = pg_cache_lookup_next(ctx, handle->pdc, handle->now_s, handle->dt_s, &entries);
793
819 - internal_fatal(handle->page && (pgc_page_data(handle->page) == DBENGINE_EMPTY_PAGE || !entries),
794 + internal_fatal(handle->page && (pgc_page_data(handle->page) == PGD_EMPTY || !entries),
795 "A page was returned, but it is empty - pg_cache_lookup_next() should be handling this case");
796
822 - if (unlikely(!handle->page || pgc_page_data(handle->page) == DBENGINE_EMPTY_PAGE || !entries))
797 + if (unlikely(!handle->page || pgc_page_data(handle->page) == PGD_EMPTY || !entries))
798 return false;
799
800 time_t page_start_time_s = pgc_page_start_time_s(handle->page);
@@ -860,8 +835,10 @@ static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_han
835
836 handle->entries = entries;
837 handle->position = position;
863 - handle->metric_data = pgc_page_data((PGC_PAGE *)handle->page);
838 handle->dt_s = page_update_every_s;
839 +
840 + pgdc_reset(&handle->pgdc, pgc_page_data(handle->page), handle->position);
841 +
842 return true;
843 }
844
@@ -890,38 +867,7 @@ STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim
867 sp.start_time_s = handle->now_s - handle->dt_s;
868 sp.end_time_s = handle->now_s;
869
893 - switch(handle->ctx->config.page_type) {
894 - case PAGE_METRICS: {
895 - storage_number n = handle->metric_data[handle->position];
896 - sp.min = sp.max = sp.sum = unpack_storage_number(n);
897 - sp.flags = n & SN_USER_FLAGS;
898 - sp.count = 1;
899 - sp.anomaly_count = is_storage_number_anomalous(n) ? 1 : 0;
900 - }
901 - break;
902 -
903 - case PAGE_TIER: {
904 - storage_number_tier1_t tier1_value = ((storage_number_tier1_t *)handle->metric_data)[handle->position];
905 - sp.flags = tier1_value.anomaly_count ? SN_FLAG_NONE : SN_FLAG_NOT_ANOMALOUS;
906 - sp.count = tier1_value.count;
907 - sp.anomaly_count = tier1_value.anomaly_count;
908 - sp.min = tier1_value.min_value;
909 - sp.max = tier1_value.max_value;
910 - sp.sum = tier1_value.sum_value;
911 - }
912 - break;
913 -
914 - // we don't know this page type
915 - default: {
916 - static bool logged = false;
917 - if(!logged) {
918 - netdata_log_error("DBENGINE: unknown page type %d found. Cannot decode it. Ignoring its metrics.", handle->ctx->config.page_type);
919 - logged = true;
920 - }
921 - storage_point_empty(sp, sp.start_time_s, sp.end_time_s);
922 - }
923 - break;
924 - }
870 + pgdc_get_next_point(&handle->pgdc, handle->position, &sp);
871
872 prepare_for_next_iteration:
873 internal_fatal(sp.end_time_s < rrddim_handle->start_time_s, "DBENGINE: this point is too old for this query");
@@ -945,8 +891,10 @@ void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrddim_hand
891 {
892 struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle;
893
948 - if (handle->page)
894 + if (handle->page) {
895 pgc_page_release(main_cache, handle->page);
896 + pgdc_reset(&handle->pgdc, NULL, UINT32_MAX);
897 + }
898
899 if(!pdc_release_and_destroy_if_unreferenced(handle->pdc, false, false))
900 __atomic_store_n(&handle->pdc->workers_should_stop, true, __ATOMIC_RELAXED);
database/engine/rrdengineapi.h
+1
@@ -20,6 +20,7 @@ extern int default_multidb_disk_quota_mb;
20 extern struct rrdengine_instance *multidb_ctx[RRD_STORAGE_TIERS];
21 extern size_t page_type_size[];
22 extern size_t tier_page_size[];
23 +extern uint8_t tier_page_type[];
24
25 #define CTX_POINT_SIZE_BYTES(ctx) page_type_size[(ctx)->config.page_type]
26
libnetdata/gorilla/fuzzer.sh
+1 -1
@@ -11,4 +11,4 @@ clang++ \
11 -fsanitize=fuzzer \
12 -o gorilla_fuzzer gorilla.cc
13
14 -./gorilla_fuzzer -workers=8 -jobs=8
14 +./gorilla_fuzzer -workers=12 -jobs=16
libnetdata/gorilla/gorilla.cc
+314 -412
@@ -17,413 +17,344 @@ static constexpr size_t bit_size() noexcept
17 return (sizeof(T) * CHAR_BIT);
18 }
19
20 -/*
21 - * Low-level bitstream operations, allowing us to read/write individual bits.
22 -*/
23 -
24 -template<typename Word>
25 -struct bit_stream_t {
26 - Word *buffer;
27 - size_t capacity;
28 - size_t position;
29 -};
30 -
31 -template<typename Word>
32 -static bit_stream_t<Word> bit_stream_new(Word *buffer, Word capacity) {
33 - bit_stream_t<Word> bs;
34 -
35 - bs.buffer = buffer;
36 - bs.capacity = capacity * bit_size<Word>();
37 - bs.position = 0;
38 -
39 - return bs;
40 -}
41 -
42 -template<typename Word>
43 -static bool bit_stream_write(bit_stream_t<Word> *bs, Word value, size_t nbits) {
44 - assert(nbits > 0 && nbits <= bit_size<Word>());
45 - assert(bs->capacity >= (bs->position + nbits));
20 +static void bit_buffer_write(uint32_t *buf, size_t pos, uint32_t v, size_t nbits)
21 +{
22 + assert(nbits > 0 && nbits <= bit_size<uint32_t>());
23
47 - if (bs->position + nbits > bs->capacity) {
48 - return false;
49 - }
24 + const size_t index = pos / bit_size<uint32_t>();
25 + const size_t offset = pos % bit_size<uint32_t>();
26
51 - const size_t index = bs->position / bit_size<Word>();
52 - const size_t offset = bs->position % bit_size<Word>();
53 - bs->position += nbits;
27 + pos += nbits;
28
29 if (offset == 0) {
56 - bs->buffer[index] = value;
30 + buf[index] = v;
31 } else {
58 - const size_t remaining_bits = bit_size<Word>() - offset;
32 + const size_t remaining_bits = bit_size<uint32_t>() - offset;
33
34 // write the lower part of the value
61 - const Word low_bits_mask = ((Word) 1 << remaining_bits) - 1;
62 - const Word lowest_bits_in_value = value & low_bits_mask;
63 - bs->buffer[index] |= (lowest_bits_in_value << offset);
35 + const uint32_t low_bits_mask = ((uint32_t) 1 << remaining_bits) - 1;
36 + const uint32_t lowest_bits_in_value = v & low_bits_mask;
37 + buf[index] |= (lowest_bits_in_value << offset);
38
39 if (nbits > remaining_bits) {
40 // write the upper part of the value
67 - const Word high_bits_mask = ~low_bits_mask;
68 - const Word highest_bits_in_value = (value & high_bits_mask) >> (remaining_bits);
69 - bs->buffer[index + 1] = highest_bits_in_value;
41 + const uint32_t high_bits_mask = ~low_bits_mask;
42 + const uint32_t highest_bits_in_value = (v & high_bits_mask) >> (remaining_bits);
43 + buf[index + 1] = highest_bits_in_value;
44 }
45 }
72 -
73 - return true;
46 }
47
76 -template<typename Word>
77 -static bool bit_stream_read(bit_stream_t<Word> *bs, Word *value, size_t nbits) {
78 - assert(nbits > 0 && nbits <= bit_size<Word>());
79 - assert(bs->capacity >= (bs->position + nbits));
48 +static void bit_buffer_read(const uint32_t *buf, size_t pos, uint32_t *v, size_t nbits)
49 +{
50 + assert(nbits > 0 && nbits <= bit_size<uint32_t>());
51
81 - if (bs->position + nbits > bs->capacity) {
82 - return false;
83 - }
52 + const size_t index = pos / bit_size<uint32_t>();
53 + const size_t offset = pos % bit_size<uint32_t>();
54
85 - const size_t index = bs->position / bit_size<Word>();
86 - const size_t offset = bs->position % bit_size<Word>();
87 - bs->position += nbits;
55 + pos += nbits;
56
57 if (offset == 0) {
90 - *value = (nbits == bit_size<Word>()) ?
91 - bs->buffer[index] :
92 - bs->buffer[index] & (((Word) 1 << nbits) - 1);
58 + *v = (nbits == bit_size<uint32_t>()) ?
59 + buf[index] :
60 + buf[index] & (((uint32_t) 1 << nbits) - 1);
61 } else {
94 - const size_t remaining_bits = bit_size<Word>() - offset;
62 + const size_t remaining_bits = bit_size<uint32_t>() - offset;
63
64 // extract the lower part of the value
65 if (nbits < remaining_bits) {
98 - *value = (bs->buffer[index] >> offset) & (((Word) 1 << nbits) - 1);
66 + *v = (buf[index] >> offset) & (((uint32_t) 1 << nbits) - 1);
67 } else {
100 - *value = (bs->buffer[index] >> offset) & (((Word) 1 << remaining_bits) - 1);
68 + *v = (buf[index] >> offset) & (((uint32_t) 1 << remaining_bits) - 1);
69 nbits -= remaining_bits;
102 - *value |= (bs->buffer[index + 1] & (((Word) 1 << nbits) - 1)) << remaining_bits;
70 + *v |= (buf[index + 1] & (((uint32_t) 1 << nbits) - 1)) << remaining_bits;
71 }
72 }
105 -
106 - return true;
73 }
74
109 -/*
110 - * High-level Gorilla codec implementation
111 -*/
112 -
113 -template<typename Word>
114 -struct bit_code_t {
115 - bit_stream_t<Word> bs;
116 - Word entries;
117 - Word prev_number;
118 - Word prev_xor;
119 - Word prev_xor_lzc;
120 -};
121 -
122 -template<typename Word>
123 -static void bit_code_init(bit_code_t<Word> *bc, Word *buffer, Word capacity) {
124 - bc->bs = bit_stream_new(buffer, capacity);
125 -
126 - bc->entries = 0;
127 - bc->prev_number = 0;
128 - bc->prev_xor = 0;
129 - bc->prev_xor_lzc = 0;
130 -
131 - // reserved two words:
132 - // Buffer[0] -> number of entries written
133 - // Buffer[1] -> number of bits written
75 +gorilla_writer_t gorilla_writer_init(gorilla_buffer_t *gbuf, size_t n)
76 +{
77 + gorilla_writer_t gw = gorilla_writer_t {
78 + .head_buffer = gbuf,
79 + .last_buffer = NULL,
80 + .prev_number = 0,
81 + .prev_xor_lzc = 0,
82 + .capacity = 0
83 + };
84
135 - bc->bs.position += 2 * bit_size<Word>();
85 + gorilla_writer_add_buffer(&gw, gbuf, n);
86 + return gw;
87 }
88
138 -template<typename Word>
139 -static bool bit_code_read(bit_code_t<Word> *bc, Word *number) {
140 - bit_stream_t<Word> *bs = &bc->bs;
141 -
142 - bc->entries++;
89 +void gorilla_writer_add_buffer(gorilla_writer_t *gw, gorilla_buffer_t *gbuf, size_t n)
90 +{
91 + gbuf->header.next = NULL;
92 + gbuf->header.entries = 0;
93 + gbuf->header.nbits = 0;
94
144 - // read the first number
145 - if (bc->entries == 1) {
146 - bool ok = bit_stream_read(bs, number, bit_size<Word>());
147 - bc->prev_number = *number;
148 - return ok;
149 - }
95 + uint32_t capacity = (n * bit_size<uint32_t>()) - (sizeof(gorilla_header_t) * CHAR_BIT);
96
151 - // process same-number bit
152 - Word is_same_number;
153 - if (!bit_stream_read(bs, &is_same_number, 1)) {
154 - return false;
155 - }
97 + gw->prev_number = 0;
98 + gw->prev_xor_lzc = 0;
99 + gw->capacity = capacity;
100
157 - if (is_same_number) {
158 - *number = bc->prev_number;
159 - return true;
160 - }
101 + if (gw->last_buffer)
102 + gw->last_buffer->header.next = gbuf;
103
162 - // proceess same-xor-lzc bit
163 - Word xor_lzc = bc->prev_xor_lzc;
164 -
165 - Word same_xor_lzc;
166 - if (!bit_stream_read(bs, &same_xor_lzc, 1)) {
167 - return false;
168 - }
104 + __atomic_store_n(&gw->last_buffer, gbuf, __ATOMIC_RELAXED);
105 +}
106
170 - if (!same_xor_lzc) {
171 - if (!bit_stream_read(bs, &xor_lzc, (bit_size<Word>() == 32) ? 5 : 6)) {
172 - return false;
173 - }
174 - }
107 +uint32_t gorilla_writer_entries(const gorilla_writer_t *gw) {
108 + uint32_t entries = 0;
109
176 - // process the non-lzc suffix
177 - Word xor_value = 0;
178 - if (!bit_stream_read(bs, &xor_value, bit_size<Word>() - xor_lzc)) {
179 - return false;
180 - }
110 + const gorilla_buffer_t *curr_gbuf = __atomic_load_n(&gw->head_buffer, __ATOMIC_SEQ_CST);
111 + do {
112 + const gorilla_buffer_t *next_gbuf = __atomic_load_n(&curr_gbuf->header.next, __ATOMIC_SEQ_CST);
113
182 - *number = (bc->prev_number ^ xor_value);
114 + entries += __atomic_load_n(&curr_gbuf->header.entries, __ATOMIC_SEQ_CST);
115
184 - bc->prev_number = *number;
185 - bc->prev_xor_lzc = xor_lzc;
186 - bc->prev_xor = xor_value;
116 + curr_gbuf = next_gbuf;
117 + } while (curr_gbuf);
118
188 - return true;
119 + return entries;
120 }
121
191 -template<typename Word>
192 -static bool bit_code_write(bit_code_t<Word> *bc, const Word number) {
193 - bit_stream_t<Word> *bs = &bc->bs;
194 - Word position = bs->position;
195 -
196 - bc->entries++;
122 +bool gorilla_writer_write(gorilla_writer_t *gw, uint32_t number)
123 +{
124 + gorilla_header_t *hdr = &gw->last_buffer->header;
125 + uint32_t *data = gw->last_buffer->data;
126
127 // this is the first number we are writing
199 - if (bc->entries == 1) {
200 - bc->prev_number = number;
201 - return bit_stream_write(bs, number, bit_size<Word>());
202 - }
203 -
204 - // write true/false based on whether we got the same number or not.
205 - if (number == bc->prev_number) {
206 - return bit_stream_write(bs, static_cast<Word>(1), 1);
207 - } else {
208 - if (bit_stream_write(bs, static_cast<Word>(0), 1) == false) {
128 + if (hdr->entries == 0) {
129 + if (hdr->nbits + bit_size<uint32_t>() >= gw->capacity)
130 return false;
210 - }
211 - }
212 -
213 - // otherwise:
214 - // - compute the non-zero xor
215 - // - find its leading-zero count
216 -
217 - Word xor_value = bc->prev_number ^ number;
218 - // FIXME: Use SFINAE
219 - Word xor_lzc = (bit_size<Word>() == 32) ? __builtin_clz(xor_value) : __builtin_clzll(xor_value);
220 - Word is_xor_lzc_same = (xor_lzc == bc->prev_xor_lzc) ? 1 : 0;
221 -
222 - if (is_xor_lzc_same) {
223 - // xor-lzc is same
224 - if (bit_stream_write(bs, static_cast<Word>(1), 1) == false) {
225 - goto RET_FALSE;
226 - }
227 - } else {
228 - // xor-lzc is different
229 - if (bit_stream_write(bs, static_cast<Word>(0), 1) == false) {
230 - goto RET_FALSE;
231 - }
232 -
233 - if (bit_stream_write(bs, xor_lzc, (bit_size<Word>() == 32) ? 5 : 6) == false) {
234 - goto RET_FALSE;
235 - }
236 - }
131 + bit_buffer_write(data, hdr->nbits, number, bit_size<uint32_t>());
132
238 - // write the bits of the XOR value without the LZC prefix
239 - if (bit_stream_write(bs, xor_value, bit_size<Word>() - xor_lzc) == false) {
240 - goto RET_FALSE;
133 + __atomic_fetch_add(&hdr->nbits, bit_size<uint32_t>(), __ATOMIC_RELAXED);
134 + __atomic_fetch_add(&hdr->entries, 1, __ATOMIC_RELAXED);
135 + gw->prev_number = number;
136 + return true;
137 }
138
243 - bc->prev_number = number;
244 - bc->prev_xor_lzc = xor_lzc;
245 - return true;
246 -
247 -RET_FALSE:
248 - bc->bs.position = position;
249 - return false;
250 -}
139 + // write true/false based on whether we got the same number or not.
140 + if (number == gw->prev_number) {
141 + if (hdr->nbits + 1 >= gw->capacity)
142 + return false;
143
252 -// only valid for writers
253 -template<typename Word>
254 -static bool bit_code_flush(bit_code_t<Word> *bc) {
255 - bit_stream_t<Word> *bs = &bc->bs;
144 + bit_buffer_write(data, hdr->nbits, static_cast<uint32_t>(1), 1);
145 + __atomic_fetch_add(&hdr->nbits, 1, __ATOMIC_RELAXED);
146 + __atomic_fetch_add(&hdr->entries, 1, __ATOMIC_RELAXED);
147 + return true;
148 + }
149
257 - Word num_entries_written = bc->entries;
258 - Word num_bits_written = bs->position;
150 + if (hdr->nbits + 1 >= gw->capacity)
151 + return false;
152 + bit_buffer_write(data, hdr->nbits, static_cast<uint32_t>(0), 1);
153 + __atomic_fetch_add(&hdr->nbits, 1, __ATOMIC_RELAXED);
154
260 - // we want to write these at the beginning
261 - bs->position = 0;
155 + uint32_t xor_value = gw->prev_number ^ number;
156 + uint32_t xor_lzc = (bit_size<uint32_t>() == 32) ? __builtin_clz(xor_value) : __builtin_clzll(xor_value);
157 + uint32_t is_xor_lzc_same = (xor_lzc == gw->prev_xor_lzc) ? 1 : 0;
158
263 - if (!bit_stream_write(bs, num_entries_written, bit_size<Word>())) {
159 + if (hdr->nbits + 1 >= gw->capacity)
160 return false;
161 + bit_buffer_write(data, hdr->nbits, is_xor_lzc_same, 1);
162 + __atomic_fetch_add(&hdr->nbits, 1, __ATOMIC_RELAXED);
163 +
164 + if (!is_xor_lzc_same) {
165 + if (hdr->nbits + 1 >= gw->capacity)
166 + return false;
167 + bit_buffer_write(data, hdr->nbits, xor_lzc, (bit_size<uint32_t>() == 32) ? 5 : 6);
168 + __atomic_fetch_add(&hdr->nbits, (bit_size<uint32_t>() == 32) ? 5 : 6, __ATOMIC_RELAXED);
169 }
170
267 - if (!bit_stream_write(bs, num_bits_written, bit_size<Word>())) {
171 + // write the bits of the XOR'd value without the LZC prefix
172 + if (hdr->nbits + (bit_size<uint32_t>() - xor_lzc) >= gw->capacity)
173 return false;
269 - }
174 + bit_buffer_write(data, hdr->nbits, xor_value, bit_size<uint32_t>() - xor_lzc);
175 + __atomic_fetch_add(&hdr->nbits, bit_size<uint32_t>() - xor_lzc, __ATOMIC_RELAXED);
176 + __atomic_fetch_add(&hdr->entries, 1, __ATOMIC_RELAXED);
177
271 - bs->position = num_bits_written;
178 + gw->prev_number = number;
179 + gw->prev_xor_lzc = xor_lzc;
180 return true;
181 }
182
275 -// only valid for readers
276 -template<typename Word>
277 -static bool bit_code_info(bit_code_t<Word> *bc, Word *num_entries_written,
278 - Word *num_bits_written) {
279 - bit_stream_t<Word> *bs = &bc->bs;
183 +gorilla_buffer_t *gorilla_writer_drop_head_buffer(gorilla_writer_t *gw) {
184 + if (!gw->head_buffer)
185 + return NULL;
186
281 - assert(bs->position == 2 * bit_size<Word>());
282 - if (bs->capacity < (2 * bit_size<Word>())) {
283 - return false;
284 - }
285 -
286 - if (num_entries_written) {
287 - *num_entries_written = bs->buffer[0];
288 - }
289 - if (num_bits_written) {
290 - *num_bits_written = bs->buffer[1];
291 - }
292 -
293 - return true;
187 + gorilla_buffer_t *curr_head = gw->head_buffer;
188 + gorilla_buffer_t *next_head = gw->head_buffer->header.next;
189 + __atomic_store_n(&gw->head_buffer, next_head, __ATOMIC_RELAXED);
190 + return curr_head;
191 }
192
296 -template<typename Word>
297 -static size_t gorilla_encode(Word *dst, Word dst_len, const Word *src, Word src_len) {
298 - bit_code_t<Word> bcw;
193 +uint32_t gorilla_writer_nbytes(const gorilla_writer_t *gw)
194 +{
195 + uint32_t nbits = 0;
196
300 - bit_code_init(&bcw, dst, dst_len);
197 + const gorilla_buffer_t *curr_gbuf = __atomic_load_n(&gw->head_buffer, __ATOMIC_SEQ_CST);
198 + do {
199 + const gorilla_buffer_t *next_gbuf = __atomic_load_n(&curr_gbuf->header.next, __ATOMIC_SEQ_CST);
200
302 - for (size_t i = 0; i != src_len; i++) {
303 - if (!bit_code_write(&bcw, src[i]))
304 - return 0;
305 - }
201 + nbits += __atomic_load_n(&curr_gbuf->header.nbits, __ATOMIC_SEQ_CST);
202
307 - if (!bit_code_flush(&bcw))
308 - return 0;
203 + curr_gbuf = next_gbuf;
204 + } while (curr_gbuf);
205
310 - return src_len;
206 + return (nbits + (CHAR_BIT - 1)) / CHAR_BIT;
207 }
208
313 -template<typename Word>
314 -static size_t gorilla_decode(Word *dst, Word dst_len, const Word *src, Word src_len) {
315 - bit_code_t<Word> bcr;
209 +bool gorilla_writer_serialize(const gorilla_writer_t *gw, uint8_t *dst, uint32_t dst_size) {
210 + const gorilla_buffer_t *curr_gbuf = gw->head_buffer;
211
317 - bit_code_init(&bcr, (Word *) src, src_len);
212 + do {
213 + const gorilla_buffer_t *next_gbuf = curr_gbuf->header.next;
214
319 - Word num_entries;
320 - if (!bit_code_info(&bcr, &num_entries, (Word *) NULL)) {
321 - return 0;
322 - }
323 - if (num_entries > dst_len) {
324 - return 0;
325 - }
326 -
327 - for (size_t i = 0; i != num_entries; i++) {
328 - if (!bit_code_read(&bcr, &dst[i]))
329 - return 0;
330 - }
215 + size_t bytes = GORILLA_BUFFER_SIZE;
216 + if (bytes > dst_size)
217 + return false;
218
332 - return num_entries;
333 -}
219 + memcpy(dst, curr_gbuf, bytes);
220 + dst += bytes;
221 + dst_size -= bytes;
222
335 -/*
336 - * Low-level public API
337 -*/
338 -
339 -// 32-bit API
223 + curr_gbuf = next_gbuf;
224 + } while (curr_gbuf);
225
341 -void bit_code_writer_u32_init(bit_code_writer_u32_t *bcw, uint32_t *buffer, uint32_t capacity) {
342 - bit_code_t<uint32_t> *bc = (bit_code_t<uint32_t> *) bcw;
343 - bit_code_init(bc, buffer, capacity);
226 + return true;
227 }
228
346 -bool bit_code_writer_u32_write(bit_code_writer_u32_t *bcw, const uint32_t number) {
347 - bit_code_t<uint32_t> *bc = (bit_code_t<uint32_t> *) bcw;
348 - return bit_code_write(bc, number);
349 -}
229 +uint32_t gorilla_buffer_patch(gorilla_buffer_t *gbuf) {
230 + gorilla_buffer_t *curr_gbuf = gbuf;
231 + uint32_t n = curr_gbuf->header.entries;
232
351 -bool bit_code_writer_u32_flush(bit_code_writer_u32_t *bcw) {
352 - bit_code_t<uint32_t> *bc = (bit_code_t<uint32_t> *) bcw;
353 - return bit_code_flush(bc);
354 -}
233 + while (curr_gbuf->header.next) {
234 + uint32_t *buf = reinterpret_cast<uint32_t *>(gbuf);
235 + gbuf = reinterpret_cast<gorilla_buffer_t *>(&buf[GORILLA_BUFFER_SLOTS]);
236
356 -void bit_code_reader_u32_init(bit_code_reader_u32_t *bcr, uint32_t *buffer, uint32_t capacity) {
357 - bit_code_t<uint32_t> *bc = (bit_code_t<uint32_t> *) bcr;
358 - bit_code_init(bc, buffer, capacity);
359 -}
237 + assert(((uintptr_t) (gbuf) % sizeof(uintptr_t)) == 0 &&
238 + "Gorilla buffer not aligned to uintptr_t");
239
361 -bool bit_code_reader_u32_read(bit_code_reader_u32_t *bcr, uint32_t *number) {
362 - bit_code_t<uint32_t> *bc = (bit_code_t<uint32_t> *) bcr;
363 - return bit_code_read(bc, number);
364 -}
240 + curr_gbuf->header.next = gbuf;
241 + curr_gbuf = curr_gbuf->header.next;
242
366 -bool bit_code_reader_u32_info(bit_code_reader_u32_t *bcr, uint32_t *num_entries_written,
367 - uint32_t *num_bits_written) {
368 - bit_code_t<uint32_t> *bc = (bit_code_t<uint32_t> *) bcr;
369 - return bit_code_info(bc, num_entries_written, num_bits_written);
243 + n += curr_gbuf->header.entries;
244 + }
245 +
246 + return n;
247 }
248
372 -// 64-bit API
249 +gorilla_reader_t gorilla_writer_get_reader(const gorilla_writer_t *gw)
250 +{
251 + const gorilla_buffer_t *buffer = __atomic_load_n(&gw->head_buffer, __ATOMIC_SEQ_CST);
252
374 -void bit_code_writer_u64_init(bit_code_writer_u64_t *bcw, uint64_t *buffer, uint64_t capacity) {
375 - bit_code_t<uint64_t> *bc = (bit_code_t<uint64_t> *) bcw;
376 - bit_code_init(bc, buffer, capacity);
377 -}
253 + uint32_t entries = __atomic_load_n(&buffer->header.entries, __ATOMIC_SEQ_CST);
254 + uint32_t capacity = __atomic_load_n(&buffer->header.nbits, __ATOMIC_SEQ_CST);
255
379 -bool bit_code_writer_u64_write(bit_code_writer_u64_t *bcw, const uint64_t number) {
380 - bit_code_t<uint64_t> *bc = (bit_code_t<uint64_t> *) bcw;
381 - return bit_code_write(bc, number);
256 + return gorilla_reader_t {
257 + .buffer = buffer,
258 + .entries = entries,
259 + .index = 0,
260 + .capacity = capacity,
261 + .position = 0,
262 + .prev_number = 0,
263 + .prev_xor_lzc = 0,
264 + .prev_xor = 0,
265 + };
266 }
267
384 -bool bit_code_writer_u64_flush(bit_code_writer_u64_t *bcw) {
385 - bit_code_t<uint64_t> *bc = (bit_code_t<uint64_t> *) bcw;
386 - return bit_code_flush(bc);
387 -}
268 +gorilla_reader_t gorilla_reader_init(gorilla_buffer_t *gbuf)
269 +{
270 + uint32_t entries = __atomic_load_n(&gbuf->header.entries, __ATOMIC_SEQ_CST);
271 + uint32_t capacity = __atomic_load_n(&gbuf->header.nbits, __ATOMIC_SEQ_CST);
272 +
273 + return gorilla_reader_t {
274 + .buffer = gbuf,
275 + .entries = entries,
276 + .index = 0,
277 + .capacity = capacity,
278 + .position = 0,
279 + .prev_number = 0,
280 + .prev_xor_lzc = 0,
281 + .prev_xor = 0,
282 + };
283 +}
284 +
285 +bool gorilla_reader_read(gorilla_reader_t *gr, uint32_t *number)
286 +{
287 + const uint32_t *data = gr->buffer->data;
288 +
289 + if (gr->index + 1 > gr->entries) {
290 + // We don't have any more entries to return. However, the writer
291 + // might have updated the buffer's entries. We need to check once
292 + // more in case more elements were added.
293 + gr->entries = __atomic_load_n(&gr->buffer->header.entries, __ATOMIC_SEQ_CST);
294 + gr->capacity = __atomic_load_n(&gr->buffer->header.nbits, __ATOMIC_SEQ_CST);
295 +
296 + // if the reader's current buffer has not been updated, we need to
297 + // check if it has a pointer to a next buffer.
298 + if (gr->index + 1 > gr->entries) {
299 + gorilla_buffer_t *next_buffer = __atomic_load_n(&gr->buffer->header.next, __ATOMIC_SEQ_CST);
300 +
301 + if (!next_buffer) {
302 + // fprintf(stderr, "Consumed reader with %zu entries from buffer %p\n (No more buffers to read from)", gr->length, gr->buffer);
303 + return false;
304 + }
305 +
306 + // fprintf(stderr, "Consumed reader with %zu entries from buffer %p\n", gr->length, gr->buffer);
307 + *gr = gorilla_reader_init(next_buffer);
308 + return gorilla_reader_read(gr, number);
309 + }
310 + }
311
389 -void bit_code_reader_u64_init(bit_code_reader_u64_t *bcr, uint64_t *buffer, uint64_t capacity) {
390 - bit_code_t<uint64_t> *bc = (bit_code_t<uint64_t> *) bcr;
391 - bit_code_init(bc, buffer, capacity);
392 -}
312 + // read the first number
313 + if (gr->index == 0) {
314 + bit_buffer_read(data, gr->position, number, bit_size<uint32_t>());
315
394 -bool bit_code_reader_u64_read(bit_code_reader_u64_t *bcr, uint64_t *number) {
395 - bit_code_t<uint64_t> *bc = (bit_code_t<uint64_t> *) bcr;
396 - return bit_code_read(bc, number);
397 -}
316 + gr->index++;
317 + gr->position += bit_size<uint32_t>();
318 + gr->prev_number = *number;
319 + return true;
320 + }
321
399 -bool bit_code_reader_u64_info(bit_code_reader_u64_t *bcr, uint64_t *num_entries_written,
400 - uint64_t *num_bits_written) {
401 - bit_code_t<uint64_t> *bc = (bit_code_t<uint64_t> *) bcr;
402 - return bit_code_info(bc, num_entries_written, num_bits_written);
403 -}
322 + // process same-number bit
323 + uint32_t is_same_number;
324 + bit_buffer_read(data, gr->position, &is_same_number, 1);
325 + gr->position++;
326
405 -/*
406 - * High-level public API
407 -*/
327 + if (is_same_number) {
328 + *number = gr->prev_number;
329 + gr->index++;
330 + return true;
331 + }
332
409 -// 32-bit API
333 + // proceess same-xor-lzc bit
334 + uint32_t xor_lzc = gr->prev_xor_lzc;
335
411 -size_t gorilla_encode_u32(uint32_t *dst, size_t dst_len, const uint32_t *src, size_t src_len) {
412 - return gorilla_encode(dst, (uint32_t) dst_len, src, (uint32_t) src_len);
413 -}
336 + uint32_t same_xor_lzc;
337 + bit_buffer_read(data, gr->position, &same_xor_lzc, 1);
338 + gr->position++;
339
415 -size_t gorilla_decode_u32(uint32_t *dst, size_t dst_len, const uint32_t *src, size_t src_len) {
416 - return gorilla_decode(dst, (uint32_t) dst_len, src, (uint32_t) src_len);
417 -}
340 + if (!same_xor_lzc) {
341 + bit_buffer_read(data, gr->position, &xor_lzc, (bit_size<uint32_t>() == 32) ? 5 : 6);
342 + gr->position += (bit_size<uint32_t>() == 32) ? 5 : 6;
343 + }
344
419 -// 64-bit API
345 + // process the non-lzc suffix
346 + uint32_t xor_value = 0;
347 + bit_buffer_read(data, gr->position, &xor_value, bit_size<uint32_t>() - xor_lzc);
348 + gr->position += bit_size<uint32_t>() - xor_lzc;
349
421 -size_t gorilla_encode_u64(uint64_t *dst, size_t dst_len, const uint64_t *src, size_t src_len) {
422 - return gorilla_encode(dst, (uint64_t) dst_len, src, (uint64_t) src_len);
423 -}
350 + *number = (gr->prev_number ^ xor_value);
351 +
352 + gr->index++;
353 + gr->prev_number = *number;
354 + gr->prev_xor_lzc = xor_lzc;
355 + gr->prev_xor = xor_value;
356
425 -size_t gorilla_decode_u64(uint64_t *dst, size_t dst_len, const uint64_t *src, size_t src_len) {
426 - return gorilla_decode(dst, (uint64_t) dst_len, src, (uint64_t) src_len);
357 + return true;
358 }
359
360 /*
@@ -451,54 +382,69 @@ static std::vector<Word> random_vector(const uint8_t *data, size_t size) {
382 return V;
383 }
384
454 -template<typename Word>
455 -static void check_equal_buffers(Word *lhs, Word lhs_size, Word *rhs, Word rhs_size) {
456 - assert((lhs_size == rhs_size) && "Buffers have different size.");
385 +class Storage {
386 +public:
387 + gorilla_buffer_t *alloc_buffer(size_t words) {
388 + uint32_t *new_buffer = new uint32_t[words]();
389 + assert(((((uintptr_t) new_buffer) % 8u) == 0) && "Unaligned buffer...");
390 + Buffers.push_back(new_buffer);
391 + return reinterpret_cast<gorilla_buffer_t *>(new_buffer);
392 + }
393
458 - for (size_t i = 0; i != lhs_size; i++) {
459 - assert((lhs[i] == rhs[i]) && "Buffers differ");
394 + void free_buffers() {
395 + for (uint32_t *buffer : Buffers) {
396 + delete[] buffer;
397 + }
398 }
461 -}
399 +
400 +private:
401 + std::vector<uint32_t *> Buffers;
402 +};
403
404 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
464 - // 32-bit tests
465 - {
466 - if (Size < 4)
467 - return 0;
468 -
469 - std::vector<uint32_t> RandomData = random_vector<uint32_t>(Data, Size);
470 - std::vector<uint32_t> EncodedData(10 * RandomData.capacity(), 0);
471 - std::vector<uint32_t> DecodedData(10 * RandomData.capacity(), 0);
472 -
473 - size_t num_entries_written = gorilla_encode_u32(EncodedData.data(), EncodedData.size(),
474 - RandomData.data(), RandomData.size());
475 - size_t num_entries_read = gorilla_decode_u32(DecodedData.data(), DecodedData.size(),
476 - EncodedData.data(), EncodedData.size());
477 -
478 - assert(num_entries_written == num_entries_read);
479 - check_equal_buffers(RandomData.data(), (uint32_t) RandomData.size(),
480 - DecodedData.data(), (uint32_t) RandomData.size());
405 + if (Size < 4)
406 + return 0;
407 +
408 + std::vector<uint32_t> RandomData = random_vector<uint32_t>(Data, Size);
409 +
410 + Storage S;
411 + size_t words_per_buffer = 8;
412 +
413 + /*
414 + * write data
415 + */
416 + gorilla_buffer_t *first_buffer = S.alloc_buffer(words_per_buffer);
417 + gorilla_writer_t gw = gorilla_writer_init(first_buffer, words_per_buffer);
418 +
419 + for (size_t i = 0; i != RandomData.size(); i++) {
420 + bool ok = gorilla_writer_write(&gw, RandomData[i]);
421 + if (ok)
422 + continue;
423 +
424 + // add new buffer
425 + gorilla_buffer_t *buffer = S.alloc_buffer(words_per_buffer);
426 + gorilla_writer_add_buffer(&gw, buffer, words_per_buffer);
427 +
428 + ok = gorilla_writer_write(&gw, RandomData[i]);
429 + assert(ok && "Could not write data to new buffer!!!");
430 }
431
483 - // 64-bit tests
484 - {
485 - if (Size < 8)
486 - return 0;
432
488 - std::vector<uint64_t> RandomData = random_vector<uint64_t>(Data, Size);
489 - std::vector<uint64_t> EncodedData(10 * RandomData.capacity(), 0);
490 - std::vector<uint64_t> DecodedData(10 * RandomData.capacity(), 0);
433 + /*
434 + * read data
435 + */
436 + gorilla_reader_t gr = gorilla_writer_get_reader(&gw);
437
492 - size_t num_entries_written = gorilla_encode_u64(EncodedData.data(), EncodedData.size(),
493 - RandomData.data(), RandomData.size());
494 - size_t num_entries_read = gorilla_decode_u64(DecodedData.data(), DecodedData.size(),
495 - EncodedData.data(), EncodedData.size());
438 + for (size_t i = 0; i != RandomData.size(); i++) {
439 + uint32_t number = 0;
440 + bool ok = gorilla_reader_read(&gr, &number);
441 + assert(ok && "Failed to read number from gorilla buffer");
442
497 - assert(num_entries_written == num_entries_read);
498 - check_equal_buffers(RandomData.data(), (uint64_t) RandomData.size(),
499 - DecodedData.data(), (uint64_t) RandomData.size());
443 + assert((number == RandomData[i])
444 + && "Read wrong number from gorilla buffer");
445 }
446
447 + S.free_buffers();
448 return 0;
449 }
450
@@ -523,17 +469,20 @@ static void BM_EncodeU32Numbers(benchmark::State& state) {
469 std::vector<uint32_t> EncodedData(10 * RandomData.capacity(), 0);
470
471 for (auto _ : state) {
526 - benchmark::DoNotOptimize(
527 - gorilla_encode_u32(EncodedData.data(), EncodedData.size(),
528 - RandomData.data(), RandomData.size())
529 - );
472 + gorilla_writer_t gw = gorilla_writer_init(
473 + reinterpret_cast<gorilla_buffer_t *>(EncodedData.data()),
474 + EncodedData.size());
475 +
476 + for (size_t i = 0; i != RandomData.size(); i++)
477 + benchmark::DoNotOptimize(gorilla_writer_write(&gw, RandomData[i]));
478 +
479 benchmark::ClobberMemory();
480 }
481
482 state.SetItemsProcessed(NumItems * state.iterations());
483 state.SetBytesProcessed(NumItems * state.iterations() * sizeof(uint32_t));
484 }
536 -BENCHMARK(BM_EncodeU32Numbers);
485 +BENCHMARK(BM_EncodeU32Numbers)->ThreadRange(1, 16)->UseRealTime();
486
487 static void BM_DecodeU32Numbers(benchmark::State& state) {
488 std::random_device rd;
@@ -547,74 +496,27 @@ static void BM_DecodeU32Numbers(benchmark::State& state) {
496 std::vector<uint32_t> EncodedData(10 * RandomData.capacity(), 0);
497 std::vector<uint32_t> DecodedData(10 * RandomData.capacity(), 0);
498
550 - gorilla_encode_u32(EncodedData.data(), EncodedData.size(),
551 - RandomData.data(), RandomData.size());
552 -
553 - for (auto _ : state) {
554 - benchmark::DoNotOptimize(
555 - gorilla_decode_u32(DecodedData.data(), DecodedData.size(),
556 - EncodedData.data(), EncodedData.size())
557 - );
558 - benchmark::ClobberMemory();
559 - }
560 -
561 - state.SetItemsProcessed(NumItems * state.iterations());
562 - state.SetBytesProcessed(NumItems * state.iterations() * sizeof(uint32_t));
563 -}
564 -// Register the function as a benchmark
565 -BENCHMARK(BM_DecodeU32Numbers);
566 -
567 -static void BM_EncodeU64Numbers(benchmark::State& state) {
568 - std::random_device rd;
569 - std::mt19937 mt(rd());
570 - std::uniform_int_distribution<uint64_t> dist(0x0, 0x0000FFFF);
499 + gorilla_writer_t gw = gorilla_writer_init(
500 + reinterpret_cast<gorilla_buffer_t *>(EncodedData.data()),
501 + EncodedData.size());
502
572 - std::vector<uint64_t> RandomData;
573 - for (size_t idx = 0; idx != 1024; idx++) {
574 - RandomData.push_back(dist(mt));
575 - }
576 - std::vector<uint64_t> EncodedData(10 * RandomData.capacity(), 0);
503 + for (size_t i = 0; i != RandomData.size(); i++)
504 + gorilla_writer_write(&gw, RandomData[i]);
505
506 for (auto _ : state) {
579 - benchmark::DoNotOptimize(
580 - gorilla_encode_u64(EncodedData.data(), EncodedData.size(),
581 - RandomData.data(), RandomData.size())
582 - );
583 - benchmark::ClobberMemory();
584 - }
507 + gorilla_reader_t gr = gorilla_reader_init(reinterpret_cast<gorilla_buffer_t *>(EncodedData.data()));
508
586 - state.SetItemsProcessed(NumItems * state.iterations());
587 - state.SetBytesProcessed(NumItems * state.iterations() * sizeof(uint64_t));
588 -}
589 -BENCHMARK(BM_EncodeU64Numbers);
590 -
591 -static void BM_DecodeU64Numbers(benchmark::State& state) {
592 - std::random_device rd;
593 - std::mt19937 mt(rd());
594 - std::uniform_int_distribution<uint64_t> dist(0x0, 0xFFFFFFFF);
595 -
596 - std::vector<uint64_t> RandomData;
597 - for (size_t idx = 0; idx != 1024; idx++) {
598 - RandomData.push_back(dist(mt));
599 - }
600 - std::vector<uint64_t> EncodedData(10 * RandomData.capacity(), 0);
601 - std::vector<uint64_t> DecodedData(10 * RandomData.capacity(), 0);
602 -
603 - gorilla_encode_u64(EncodedData.data(), EncodedData.size(),
604 - RandomData.data(), RandomData.size());
509 + for (size_t i = 0; i != RandomData.size(); i++) {
510 + uint32_t number = 0;
511 + benchmark::DoNotOptimize(gorilla_reader_read(&gr, &number));
512 + }
513
606 - for (auto _ : state) {
607 - benchmark::DoNotOptimize(
608 - gorilla_decode_u64(DecodedData.data(), DecodedData.size(),
609 - EncodedData.data(), EncodedData.size())
610 - );
514 benchmark::ClobberMemory();
515 }
516
517 state.SetItemsProcessed(NumItems * state.iterations());
615 - state.SetBytesProcessed(NumItems * state.iterations() * sizeof(uint64_t));
518 + state.SetBytesProcessed(NumItems * state.iterations() * sizeof(uint32_t));
519 }
617 -// Register the function as a benchmark
618 -BENCHMARK(BM_DecodeU64Numbers);
520 +BENCHMARK(BM_DecodeU32Numbers)->ThreadRange(1, 16)->UseRealTime();
521
522 #endif /* ENABLE_BENCHMARK */
libnetdata/gorilla/gorilla.h
+47 -30
@@ -11,47 +11,64 @@
11 extern "C" {
12 #endif
13
14 -/*
15 - * Low-level public API
16 -*/
14 +struct gorilla_buffer;
15
18 -// 32-bit API
16 +typedef struct {
17 + struct gorilla_buffer *next;
18 + uint32_t entries;
19 + uint32_t nbits;
20 +} gorilla_header_t;
21
20 -typedef struct bit_code_writer_u32 bit_code_writer_u32_t;
21 -typedef struct bit_code_reader_u32 bit_code_reader_u32_t;
22 +typedef struct gorilla_buffer {
23 + gorilla_header_t header;
24 + uint32_t data[];
25 +} gorilla_buffer_t;
26
23 -void bit_code_writer_u32_init(bit_code_writer_u32_t *bcw, uint32_t *buffer, uint32_t capacity);
24 -bool bit_code_writer_u32_write(bit_code_writer_u32_t *bcw, const uint32_t number);
25 -bool bit_code_writer_u32_flush(bit_code_writer_u32_t *bcw);
27 +typedef struct {
28 + gorilla_buffer_t *head_buffer;
29 + gorilla_buffer_t *last_buffer;
30
27 -void bit_code_reader_u32_init(bit_code_reader_u32_t *bcr, uint32_t *buffer, uint32_t capacity);
28 -bool bit_code_reader_u32_read(bit_code_reader_u32_t *bcr, uint32_t *number);
29 -bool bit_code_reader_u32_info(bit_code_reader_u32_t *bcr, uint32_t *num_entries_written,
30 - uint64_t *num_bits_written);
31 + uint32_t prev_number;
32 + uint32_t prev_xor_lzc;
33
32 -// 64-bit API
34 + // in bits
35 + uint32_t capacity;
36 +} gorilla_writer_t;
37
34 -typedef struct bit_code_writer_u64 bit_code_writer_u64_t;
35 -typedef struct bit_code_reader_u64 bit_code_reader_u64_t;
38 +typedef struct {
39 + const gorilla_buffer_t *buffer;
40
37 -void bit_code_writer_u64_init(bit_code_writer_u64_t *bcw, uint64_t *buffer, uint64_t capacity);
38 -bool bit_code_writer_u64_write(bit_code_writer_u64_t *bcw, const uint64_t number);
39 -bool bit_code_writer_u64_flush(bit_code_writer_u64_t *bcw);
41 + // number of values
42 + size_t entries;
43 + size_t index;
44
41 -void bit_code_reader_u64_init(bit_code_reader_u64_t *bcr, uint64_t *buffer, uint64_t capacity);
42 -bool bit_code_reader_u64_read(bit_code_reader_u64_t *bcr, uint64_t *number);
43 -bool bit_code_reader_u64_info(bit_code_reader_u64_t *bcr, uint64_t *num_entries_written,
44 - uint64_t *num_bits_written);
45 + // in bits
46 + size_t capacity; // FIXME: this not needed on the reader's side
47 + size_t position;
48
46 -/*
47 - * High-level public API
48 -*/
49 + uint32_t prev_number;
50 + uint32_t prev_xor_lzc;
51 + uint32_t prev_xor;
52 +} gorilla_reader_t;
53
50 -size_t gorilla_encode_u32(uint32_t *dst, size_t dst_len, const uint32_t *src, size_t src_len);
51 -size_t gorilla_decode_u32(uint32_t *dst, size_t dst_len, const uint32_t *src, size_t src_len);
54 +gorilla_writer_t gorilla_writer_init(gorilla_buffer_t *gbuf, size_t n);
55 +void gorilla_writer_add_buffer(gorilla_writer_t *gw, gorilla_buffer_t *gbuf, size_t n);
56 +bool gorilla_writer_write(gorilla_writer_t *gw, uint32_t number);
57 +uint32_t gorilla_writer_entries(const gorilla_writer_t *gw);
58
53 -size_t gorilla_encode_u64(uint64_t *dst, size_t dst_len, const uint64_t *src, size_t src_len);
54 -size_t gorilla_decode_u64(uint64_t *dst, size_t dst_len, const uint64_t *src, size_t src_len);
59 +gorilla_reader_t gorilla_writer_get_reader(const gorilla_writer_t *gw);
60 +
61 +gorilla_buffer_t *gorilla_writer_drop_head_buffer(gorilla_writer_t *gw);
62 +
63 +uint32_t gorilla_writer_nbytes(const gorilla_writer_t *gw);
64 +bool gorilla_writer_serialize(const gorilla_writer_t *gw, uint8_t *dst, uint32_t dst_size);
65 +
66 +uint32_t gorilla_buffer_patch(gorilla_buffer_t *buf);
67 +gorilla_reader_t gorilla_reader_init(gorilla_buffer_t *buf);
68 +bool gorilla_reader_read(gorilla_reader_t *gr, uint32_t *number);
69 +
70 +#define GORILLA_BUFFER_SLOTS 128
71 +#define GORILLA_BUFFER_SIZE (GORILLA_BUFFER_SLOTS * sizeof(uint32_t))
72
73 #ifdef __cplusplus
74 }
netdata-installer.sh
+8 -3
@@ -279,6 +279,7 @@ DONOTWAIT=0
279 NETDATA_PREFIX=
280 LIBS_ARE_HERE=0
281 NETDATA_ENABLE_ML=""
282 +NETDATA_ENABLE_GTESTS=0
283 NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS-}"
284 RELEASE_CHANNEL="nightly" # valid values are 'nightly' and 'stable'
285 IS_NETDATA_STATIC_BINARY="${IS_NETDATA_STATIC_BINARY:-"no"}"
@@ -333,9 +334,13 @@ while [ -n "${1}" ]; do
334 NETDATA_CONFIGURE_OPTIONS="$(echo "${NETDATA_CONFIGURE_OPTIONS%--enable-ml)}" | sed 's/$/ --enable-ml/g')"
335 NETDATA_ENABLE_ML=1
336 ;;
336 - "--disable-ml")
337 - NETDATA_CONFIGURE_OPTIONS="$(echo "${NETDATA_CONFIGURE_OPTIONS%--disable-ml)}" | sed 's/$/ --disable-ml/g')"
338 - NETDATA_ENABLE_ML=0
337 + "--enable-gtests")
338 + NETDATA_CONFIGURE_OPTIONS="$(echo "${NETDATA_CONFIGURE_OPTIONS%--enable-gtests)}" | sed 's/$/ --enable-gtests/g')"
339 + NETDATA_ENABLE_GTESTS=1
340 + ;;
341 + "--disable-gtests")
342 + NETDATA_CONFIGURE_OPTIONS="$(echo "${NETDATA_CONFIGURE_OPTIONS%--disable-gtests)}" | sed 's/$/ --disable-gtests/g')"
343 + NETDATA_ENABLE_GTESTS=0
344 ;;
345 "--disable-lto") NETDATA_CONFIGURE_OPTIONS="$(echo "${NETDATA_CONFIGURE_OPTIONS%--disable-lto)}" | sed 's/$/ --disable-lto/g')" ;;
346 "--disable-x86-sse") NETDATA_CONFIGURE_OPTIONS="$(echo "${NETDATA_CONFIGURE_OPTIONS%--disable-x86-sse)}" | sed 's/$/ --disable-x86-sse/g')" ;;