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')" ;;