@cryptotaxi247 / netdata-1 / commits / 14318932c

UUIDMap (#19307)

* added uuidmap registry of uuids, giving a uint32_t * writer code in mrg is now used only in NETDATA_INTERNAL_CHECKS * working mrg with uuidmap * rrd contexts now uses uuidmap; refcount has been abstracted everywhere * uuidmap uses get before write locking; uuidmap unittest * uuidmap supports 256 partitions * added uuidmap to memory accounting; simplified mrg memory accounting * uuidmap memory accounting * rrdddim now uses uuidmap; cleanup rrdmetric and rrdinstance; rrddim, rrdmetric, rrdinstance now free uuidmap entries on cleanup; mrg left-overs of JudyHS are removed; mrg uses the same number of partitions as uuidmap and actually the same partition per uuid * fix bad merge * db ram/alloc now uses judyL instead of judyHS * storage engine function using UUIDMAP_ID * use waitq in sender * fix warning * hot pages may become dirty prematurely during shutdown * set uuidmap partitions to 8 * reuse old uuidmap metric ids * do not get before add in mrg

Costa Tsaousis committed Jan 14, 2025 at 12:39 UTC 14318932c41ebaa215deef6d7d985ca0f8984e3b
46 files changed +1231 -497
CMakeLists.txt
+4
@@ -971,6 +971,10 @@ set(LIBNETDATA_FILES
971 src/libnetdata/locks/waitq.h
972 src/libnetdata/object-state/object-state.c
973 src/libnetdata/object-state/object-state.h
974 + src/libnetdata/uuid/uuidmap.c
975 + src/libnetdata/uuid/uuidmap.h
976 + src/libnetdata/atomics/refcount.h
977 + src/libnetdata/log/nd_log-fatal.h
978 src/libnetdata/locks/benchmark.c
979 src/libnetdata/locks/benchmark.h
980 src/libnetdata/locks/benchmark-rw.c
src/daemon/main.c
+5
@@ -384,6 +384,7 @@ int netdata_main(int argc, char **argv) {
384 if (uuid_unittest()) return 1;
385 if (dyncfg_unittest()) return 1;
386 if (unittest_waiting_queue()) return 1;
387 + if (uuidmap_unittest()) return 1;
388 sqlite_library_shutdown();
389 fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
390 return 0;
@@ -403,6 +404,10 @@ int netdata_main(int argc, char **argv) {
404 unittest_running = true;
405 return unittest_waiting_queue();
406 }
407 + else if(strcmp(optarg, "uuidmaptest") == 0) {
408 + unittest_running = true;
409 + return uuidmap_unittest();
410 + }
411 else if(strcmp(optarg, "lockstest") == 0) {
412 unittest_running = true;
413 return locks_stress_test();
src/daemon/pulse/pulse-aral.c
+1
@@ -61,6 +61,7 @@ void pulse_aral_unregister(ARAL *ar) {
61 void pulse_aral_init(void) {
62 pulse_aral_register_statistics(aral_by_size_statistics(), "by-size");
63 pulse_aral_register_statistics(judy_aral_statistics(), "judy");
64 + pulse_aral_register_statistics(uuidmap_aral_statistics(), "uuidmap");
65 }
66
67 void pulse_aral_do(bool extended) {
src/daemon/pulse/pulse-daemon-memory.c
+12 -1
@@ -40,6 +40,7 @@ void pulse_daemon_memory_do(bool extended) {
40 #else
41 static RRDDIM *rd_metadata = NULL;
42 #endif
43 + static RRDDIM *rd_uuid = NULL;
44 static RRDDIM *rd_labels = NULL; // labels use dictionary like statistics, but it is not ARAL based dictionary
45 static RRDDIM *rd_ml = NULL;
46 static RRDDIM *rd_strings = NULL;
@@ -82,6 +83,7 @@ void pulse_daemon_memory_do(bool extended) {
83 #else
84 rd_metadata = rrddim_add(st_memory, "metadata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
85 #endif
86 + rd_uuid = rrddim_add(st_memory, "uuid", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
87 rd_labels = rrddim_add(st_memory, "labels", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
88 rd_ml = rrddim_add(st_memory, "ML", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
89 rd_strings = rrddim_add(st_memory, "strings", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
@@ -107,7 +109,10 @@ void pulse_daemon_memory_do(bool extended) {
109 netdata_buffers_statistics.buffers_streaming +
110 netdata_buffers_statistics.cbuffers_streaming +
111 netdata_buffers_statistics.buffers_web +
110 - replication_sender_allocated_buffers() + aral_by_size_free_bytes() + judy_aral_free_bytes();
112 + replication_sender_allocated_buffers() +
113 + aral_by_size_free_bytes() +
114 + judy_aral_free_bytes() +
115 + uuidmap_free_bytes();
116
117 sqlite3_int64 sqlite3_memory_used_current = 0, sqlite3_memory_used_highwater = 0;
118 sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &sqlite3_memory_used_current, &sqlite3_memory_used_highwater, 1);
@@ -159,6 +164,9 @@ void pulse_daemon_memory_do(bool extended) {
164 rrddim_set_by_pointer(st_memory, rd_metadata, (collected_number)metadata);
165 #endif
166
167 + rrddim_set_by_pointer(st_memory, rd_uuid,
168 + (collected_number)uuidmap_memory());
169 +
170 // labels use dictionary like statistics, but it is not ARAL based dictionary
171 rrddim_set_by_pointer(st_memory, rd_labels,
172 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_rrdlabels));
@@ -211,6 +219,7 @@ void pulse_daemon_memory_do(bool extended) {
219 static RRDDIM *rd_buffers_web = NULL;
220 static RRDDIM *rd_buffers_aral = NULL;
221 static RRDDIM *rd_buffers_judy = NULL;
222 + static RRDDIM *rd_buffers_uuid = NULL;
223
224 if (unlikely(!st_memory_buffers)) {
225 st_memory_buffers = rrdset_create_localhost(
@@ -241,6 +250,7 @@ void pulse_daemon_memory_do(bool extended) {
250 rd_buffers_web = rrddim_add(st_memory_buffers, "web", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
251 rd_buffers_aral = rrddim_add(st_memory_buffers, "aral-by-size free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
252 rd_buffers_judy = rrddim_add(st_memory_buffers, "aral-judy free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
253 + rd_buffers_uuid = rrddim_add(st_memory_buffers, "uuid", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
254 }
255
256 // the sum of all these needs to be above at the total buffers calculation
@@ -258,6 +268,7 @@ void pulse_daemon_memory_do(bool extended) {
268 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_replication, (collected_number)replication_sender_allocated_buffers());
269 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_aral, (collected_number)aral_by_size_free_bytes());
270 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_judy, (collected_number)judy_aral_free_bytes());
271 + rrddim_set_by_pointer(st_memory_buffers, rd_buffers_uuid, (collected_number)uuidmap_free_bytes());
272
273 rrdset_done(st_memory_buffers);
274 }
src/daemon/pulse/pulse-db-dbengine.c
-9
@@ -91,9 +91,6 @@ struct dbengine2_cache_pointers {
91 RRDDIM *rd_pgc_waste_flushes_cancelled;
92 RRDDIM *rd_pgc_waste_insert_spins;
93 RRDDIM *rd_pgc_waste_evict_spins;
94 - RRDDIM *rd_pgc_waste_release_spins;
95 - RRDDIM *rd_pgc_waste_acquire_spins;
96 - RRDDIM *rd_pgc_waste_delete_spins;
94 };
95
96 static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *ptrs, struct pgc_statistics *pgc_stats, struct pgc_statistics *pgc_stats_old __maybe_unused, const char *name, int priority) {
@@ -563,10 +560,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
560
561 ptrs->rd_pgc_waste_evict_relocated = rrddim_add(ptrs->st_pgc_waste, "evict relocated", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
562 ptrs->rd_pgc_waste_flushes_cancelled = rrddim_add(ptrs->st_pgc_waste, "flushes cancelled", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
566 - ptrs->rd_pgc_waste_acquire_spins = rrddim_add(ptrs->st_pgc_waste, "acquire spins", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
567 - ptrs->rd_pgc_waste_release_spins = rrddim_add(ptrs->st_pgc_waste, "release spins", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
563 ptrs->rd_pgc_waste_insert_spins = rrddim_add(ptrs->st_pgc_waste, "insert spins", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
569 - ptrs->rd_pgc_waste_delete_spins = rrddim_add(ptrs->st_pgc_waste, "delete spins", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
564 ptrs->rd_pgc_waste_evict_spins = rrddim_add(ptrs->st_pgc_waste, "evict useless spins", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
565 ptrs->rd_pgc_waste_evict_thread_signals = rrddim_add(ptrs->st_pgc_waste, "evict thread signals", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
566 ptrs->rd_pgc_waste_evict_inline_on_add = rrddim_add(ptrs->st_pgc_waste, "evict inline on add", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
@@ -582,10 +576,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
576
577 rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_relocated, (collected_number)pgc_stats->p2_waste_evict_relocated);
578 rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flushes_cancelled, (collected_number)pgc_stats->p2_waste_flushes_cancelled);
585 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_acquire_spins, (collected_number)pgc_stats->p2_waste_acquire_spins);
586 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_release_spins, (collected_number)pgc_stats->p2_waste_release_spins);
579 rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_insert_spins, (collected_number)pgc_stats->p2_waste_insert_spins);
588 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_delete_spins, (collected_number)pgc_stats->p2_waste_delete_spins);
580 rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_spins, (collected_number)pgc_stats->p2_waste_evict_useless_spins);
581 rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_thread_signals, (collected_number)pgc_stats->p2_waste_evict_thread_signals);
582 rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_inline_on_add, (collected_number)pgc_stats->p2_waste_evictions_inline_on_add);
src/daemon/service.c
+1 -1
@@ -38,7 +38,7 @@ static void svc_rrddim_obsolete_to_archive(RRDDIM *rd) {
38 /* only a collector can mark a chart as obsolete, so we must remove the reference */
39 if (!rrddim_finalize_collection_and_check_retention(rd)) {
40 /* This metric has no data and no references */
41 - metaqueue_delete_dimension_uuid(&rd->metric_uuid);
41 + metaqueue_delete_dimension_uuid(uuidmap_uuid_ptr(rd->uuid));
42 }
43 else {
44 /* Do not delete this dimension */
src/database/contexts/api_v1_contexts.c
+2 -2
@@ -88,7 +88,7 @@ static inline int rrdmetric_to_json_callback(const DICTIONARY_ITEM *item, void *
88
89 if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
90 char uuid[UUID_STR_LEN];
91 - uuid_unparse(rm->uuid, uuid);
91 + uuid_unparse(*uuidmap_uuid_ptr(rm->uuid), uuid);
92 buffer_json_member_add_string(wb, "uuid", uuid);
93 }
94
@@ -188,7 +188,7 @@ static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void
188
189 if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
190 char uuid[UUID_STR_LEN];
191 - uuid_unparse(ri->uuid, uuid);
191 + uuid_unparse(*uuidmap_uuid_ptr(ri->uuid), uuid);
192 buffer_json_member_add_string(wb, "uuid", uuid);
193 }
194
src/database/contexts/contexts-loading.c
+2 -3
@@ -38,14 +38,13 @@ static void rrdinstance_load_dimension_callback(SQL_DIMENSION_DATA *sd, void *da
38 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
39
40 RRDMETRIC trm = {
41 + .uuid = uuidmap_create(sd->dim_id),
42 .id = string_strdupz(sd->id),
43 .name = string_strdupz(sd->name),
44 .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomic
45 };
46 if(sd->hidden) trm.flags |= RRD_FLAG_HIDDEN;
47
47 - uuid_copy(trm.uuid, sd->dim_id);
48 -
48 dictionary_set(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm));
49
50 rrdinstance_release(ria);
@@ -67,6 +66,7 @@ static void rrdinstance_load_instance_callback(SQL_CHART_DATA *sc, void *data) {
66 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
67
68 RRDINSTANCE tri = {
69 + .uuid = uuidmap_create(sc->chart_id),
70 .id = string_strdupz(sc->id),
71 .name = string_strdupz(sc->name),
72 .title = string_strdupz(sc->title),
@@ -77,7 +77,6 @@ static void rrdinstance_load_instance_callback(SQL_CHART_DATA *sc, void *data) {
77 .update_every_s = sc->update_every,
78 .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics
79 };
80 - uuid_copy(tri.uuid, sc->chart_id);
80
81 RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, sc->id, &tri, sizeof(tri));
82
src/database/contexts/instance.c
+28 -42
@@ -39,7 +39,7 @@ inline RRDLABELS *rrdinstance_acquired_labels(RRDINSTANCE_ACQUIRED *ria) {
39 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
40 if (rrd_flag_check(ri, RRD_FLAG_OWN_LABELS | RRD_FLAG_DEMAND_LABELS)) {
41 rrd_flag_clear(ri, RRD_FLAG_DEMAND_LABELS);
42 - load_instance_labels_on_demand(&ri->uuid, ri);
42 + load_instance_labels_on_demand(uuidmap_uuid_ptr(ri->uuid), ri);
43 rrdinstance_trigger_updates(ri, __FUNCTION__ );
44 }
45 return ri->rrdlabels;
@@ -81,12 +81,15 @@ static void rrdinstance_free(RRDINSTANCE *ri) {
81 string_freez(ri->title);
82 string_freez(ri->units);
83 string_freez(ri->family);
84 + uuidmap_free(ri->uuid);
85
86 ri->id = NULL;
87 ri->name = NULL;
88 ri->title = NULL;
89 ri->units = NULL;
90 ri->family = NULL;
91 + ri->uuid = 0;
92 +
93 ri->rc = NULL;
94 ri->rrdlabels = NULL;
95 ri->rrdmetrics = NULL;
@@ -148,58 +151,36 @@ static bool rrdinstance_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
151 "RRDINSTANCE: '%s' cannot change id to '%s'",
152 string2str(ri->id), string2str(ri_new->id));
153
151 - if(!uuid_eq(ri->uuid, ri_new->uuid)) {
154 + if(ri->uuid != ri_new->uuid) {
155 #ifdef NETDATA_INTERNAL_CHECKS
156 char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
154 - uuid_unparse(ri->uuid, uuid1);
155 - uuid_unparse(ri_new->uuid, uuid2);
157 + uuid_unparse(*uuidmap_uuid_ptr(ri->uuid), uuid1);
158 + uuid_unparse(*uuidmap_uuid_ptr(ri_new->uuid), uuid2);
159 internal_error(true, "RRDINSTANCE: '%s' of host '%s' changed UUID from '%s' to '%s'",
160 string2str(ri->id), rrdhost_hostname(ri->rc->rrdhost), uuid1, uuid2);
161 #endif
162
160 - uuid_copy(ri->uuid, ri_new->uuid);
163 + SWAP(ri->uuid, ri_new->uuid);
164 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
165 }
166
164 - if(ri->rrdset && ri_new->rrdset && ri->rrdset != ri_new->rrdset) {
165 - ri->rrdset = ri_new->rrdset;
166 - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
167 - }
168 -
169 -#ifdef NETDATA_INTERNAL_CHECKS
170 - if(ri->rrdset && !uuid_eq(ri->uuid, ri->rrdset->chart_uuid)) {
171 - char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
172 - uuid_unparse(ri->uuid, uuid1);
173 - uuid_unparse(ri->rrdset->chart_uuid, uuid2);
174 - internal_error(true, "RRDINSTANCE: '%s' is linked to RRDSET '%s' but they have different UUIDs. RRDINSTANCE has '%s', RRDSET has '%s'", string2str(ri->id), rrdset_id(ri->rrdset), uuid1, uuid2);
175 - }
176 -#endif
177 -
167 if(ri->name != ri_new->name) {
179 - STRING *old = ri->name;
180 - ri->name = string_dup(ri_new->name);
181 - string_freez(old);
168 + SWAP(ri->name, ri_new->name);
169 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
170 }
171
172 if(ri->title != ri_new->title) {
186 - STRING *old = ri->title;
187 - ri->title = string_dup(ri_new->title);
188 - string_freez(old);
173 + SWAP(ri->title, ri_new->title);
174 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
175 }
176
177 if(ri->units != ri_new->units) {
193 - STRING *old = ri->units;
194 - ri->units = string_dup(ri_new->units);
195 - string_freez(old);
178 + SWAP(ri->units, ri_new->units);
179 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
180 }
181
182 if(ri->family != ri_new->family) {
200 - STRING *old = ri->family;
201 - ri->family = string_dup(ri_new->family);
202 - string_freez(old);
183 + SWAP(ri->family, ri_new->family);
184 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
185 }
186
@@ -218,6 +199,11 @@ static bool rrdinstance_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
199 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
200 }
201
202 + if(ri->rrdset && ri_new->rrdset && ri->rrdset != ri_new->rrdset) {
203 + ri->rrdset = ri_new->rrdset;
204 + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
205 + }
206 +
207 if(ri->rrdset != ri_new->rrdset) {
208 ri->rrdset = ri_new->rrdset;
209
@@ -325,18 +311,18 @@ inline void rrdinstance_from_rrdset(RRDSET *st) {
311 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
312
313 RRDINSTANCE tri = {
328 - .id = string_dup(st->id),
329 - .name = string_dup(st->name),
330 - .units = string_dup(st->units),
331 - .family = string_dup(st->family),
332 - .title = string_dup(st->title),
333 - .chart_type = st->chart_type,
334 - .priority = st->priority,
335 - .update_every_s = st->update_every,
336 - .flags = RRD_FLAG_NONE, // no need for atomics
337 - .rrdset = st,
314 + .uuid = uuidmap_create(st->chart_uuid),
315 + .id = string_dup(st->id),
316 + .name = string_dup(st->name),
317 + .units = string_dup(st->units),
318 + .family = string_dup(st->family),
319 + .title = string_dup(st->title),
320 + .chart_type = st->chart_type,
321 + .priority = st->priority,
322 + .update_every_s = st->update_every,
323 + .flags = RRD_FLAG_NONE, // no need for atomics
324 + .rrdset = st,
325 };
339 - uuid_copy(tri.uuid, st->chart_uuid);
326
327 RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, string2str(tri.id), &tri, sizeof(tri));
328
src/database/contexts/internal.h
+9 -8
@@ -211,7 +211,8 @@ rrd_flags_replace_atomic(RRD_FLAGS *flags, RRD_FLAGS desired) {
211
212
213 typedef struct rrdmetric {
214 - nd_uuid_t uuid;
214 + UUIDMAP_ID uuid;
215 + RRD_FLAGS flags;
216
217 STRING *id;
218 STRING *name;
@@ -220,30 +221,30 @@ typedef struct rrdmetric {
221
222 time_t first_time_s;
223 time_t last_time_s;
223 - RRD_FLAGS flags;
224
225 struct rrdinstance *ri;
226 } RRDMETRIC;
227
228 typedef struct rrdinstance {
229 - nd_uuid_t uuid;
229 + UUIDMAP_ID uuid;
230 + int update_every_s; // data collection frequency
231 +
232 + RRD_FLAGS flags; // flags related to this instance
233 + uint32_t priority:24;
234 + RRDSET_TYPE chart_type;
235
236 STRING *id;
237 STRING *name;
238 STRING *title;
239 STRING *units;
240 STRING *family;
236 - uint32_t priority:24;
237 - RRDSET_TYPE chart_type;
241
239 - RRD_FLAGS flags; // flags related to this instance
242 time_t first_time_s;
243 time_t last_time_s;
244
243 - time_t update_every_s; // data collection frequency
245 RRDSET *rrdset; // pointer to RRDSET when collected, or NULL
246
246 - RRDLABELS *rrdlabels; // linked to RRDSET->chart_labels or own version
247 + RRDLABELS *rrdlabels; // linked to RRDSET->chart_labels or own version
248
249 struct rrdcontext *rc;
250 DICTIONARY *rrdmetrics;
src/database/contexts/metric.c
+9 -20
@@ -66,10 +66,12 @@ inline time_t rrdmetric_acquired_last_entry(RRDMETRIC_ACQUIRED *rma) {
66 static void rrdmetric_free(RRDMETRIC *rm) {
67 string_freez(rm->id);
68 string_freez(rm->name);
69 + uuidmap_free(rm->uuid);
70
71 rm->id = NULL;
72 rm->name = NULL;
73 rm->ri = NULL;
74 + rm->uuid = 0;
75 }
76
77 // called when this rrdmetric is inserted to the rrdmetrics dictionary of a rrdinstance
@@ -114,11 +116,11 @@ static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
116 "RRDMETRIC: '%s' cannot change id to '%s'",
117 string2str(rm->id), string2str(rm_new->id));
118
117 - if(!uuid_eq(rm->uuid, rm_new->uuid)) {
119 + if(rm->uuid != rm_new->uuid) {
120 #ifdef NETDATA_INTERNAL_CHECKS
121 char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
120 - uuid_unparse(rm->uuid, uuid1);
121 - uuid_unparse(rm_new->uuid, uuid2);
122 + uuid_unparse(*uuidmap_uuid_ptr(rm->uuid), uuid1);
123 + uuid_unparse(*uuidmap_uuid_ptr(rm_new->uuid), uuid2);
124
125 time_t old_first_time_s = 0;
126 time_t old_last_time_s = 0;
@@ -127,8 +129,6 @@ static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
129 old_last_time_s = rm->last_time_s;
130 }
131
130 - uuid_copy(rm->uuid, rm_new->uuid);
131 -
132 time_t new_first_time_s = 0;
133 time_t new_last_time_s = 0;
134 if(rrdmetric_update_retention(rm)) {
@@ -144,9 +144,9 @@ static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
144 , uuid1, old_first_time_s, old_last_time_s, old_last_time_s - old_first_time_s
145 , uuid2, new_first_time_s, new_last_time_s, new_last_time_s - new_first_time_s
146 );
147 -#else
148 - uuid_copy(rm->uuid, rm_new->uuid);
147 #endif
148 +
149 + SWAP(rm->uuid, rm_new->uuid);
150 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
151 }
152
@@ -155,22 +155,11 @@ static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
155 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
156 }
157
158 -#ifdef NETDATA_INTERNAL_CHECKS
159 - if(rm->rrddim && !uuid_eq(rm->uuid, rm->rrddim->metric_uuid)) {
160 - char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
161 - uuid_unparse(rm->uuid, uuid1);
162 - uuid_unparse(rm_new->uuid, uuid2);
163 - internal_error(true, "RRDMETRIC: '%s' is linked to RRDDIM '%s' but they have different UUIDs. RRDMETRIC has '%s', RRDDIM has '%s'", string2str(rm->id), rrddim_id(rm->rrddim), uuid1, uuid2);
164 - }
165 -#endif
166 -
158 if(rm->rrddim != rm_new->rrddim)
159 rm->rrddim = rm_new->rrddim;
160
161 if(rm->name != rm_new->name) {
171 - STRING *old = rm->name;
172 - rm->name = string_dup(rm_new->name);
173 - string_freez(old);
162 + SWAP(rm->name, rm_new->name);
163 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
164 }
165
@@ -251,12 +240,12 @@ void rrdmetric_from_rrddim(RRDDIM *rd) {
240 RRDINSTANCE *ri = rrdinstance_acquired_value(rd->rrdset->rrdcontexts.rrdinstance);
241
242 RRDMETRIC trm = {
243 + .uuid = uuidmap_dup(rd->uuid),
244 .id = string_dup(rd->id),
245 .name = string_dup(rd->name),
246 .flags = RRD_FLAG_NONE, // no need for atomics
247 .rrddim = rd,
248 };
259 - uuid_copy(trm.uuid, rd->metric_uuid);
249
250 RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)dictionary_set_and_acquire_item(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm));
251
src/database/contexts/query_target.c
+1 -1
@@ -254,7 +254,7 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON
254 if(rm->rrddim && rm->rrddim->tiers[tier].smh)
255 tier_retention[tier].smh = eng->api.metric_dup(rm->rrddim->tiers[tier].smh);
256 else
257 - tier_retention[tier].smh = eng->api.metric_get(qn->rrdhost->db[tier].si, &rm->uuid);
257 + tier_retention[tier].smh = eng->api.metric_get_by_id(qn->rrdhost->db[tier].si, rm->uuid);
258
259 if(tier_retention[tier].smh) {
260 tier_retention[tier].db_first_time_s = storage_engine_oldest_time_s(tier_retention[tier].eng->seb, tier_retention[tier].smh);
src/database/contexts/rrdcontext.c
+2 -2
@@ -131,7 +131,7 @@ int rrdcontext_find_dimension_uuid(RRDSET *st, const char *id, nd_uuid_t *store_
131
132 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
133
134 - uuid_copy(*store_uuid, rm->uuid);
134 + uuidmap_uuid(rm->uuid, *store_uuid);
135
136 rrdmetric_release(rma);
137 rrdinstance_release(ria);
@@ -155,7 +155,7 @@ int rrdcontext_find_chart_uuid(RRDSET *st, nd_uuid_t *store_uuid) {
155 }
156
157 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
158 - uuid_copy(*store_uuid, ri->uuid);
158 + uuidmap_uuid(ri->uuid, *store_uuid);
159
160 rrdinstance_release(ria);
161 rrdcontext_release(rca);
src/database/contexts/worker.c
+1 -1
@@ -150,7 +150,7 @@ bool rrdmetric_update_retention(RRDMETRIC *rm) {
150 STORAGE_ENGINE *eng = rrdhost->db[tier].eng;
151
152 time_t first_time_t = 0, last_time_t = 0;
153 - if (eng->api.metric_retention_by_uuid(rrdhost->db[tier].si, &rm->uuid, &first_time_t, &last_time_t)) {
153 + if (eng->api.metric_retention_by_id(rrdhost->db[tier].si, rm->uuid, &first_time_t, &last_time_t)) {
154 if (first_time_t > 0 && first_time_t < min_first_time_t)
155 min_first_time_t = first_time_t;
156
src/database/engine/cache.c
+19 -99
@@ -14,9 +14,6 @@
14 *
15 */
16
17 -typedef int32_t REFCOUNT;
18 -#define REFCOUNT_DICONNECTED (-100)
19 -
17 // to use ARAL uncomment the following line:
18 #if !defined(FSANITIZE_ADDRESS)
19 #define PGC_WITH_ARAL 1
@@ -869,58 +866,28 @@ static inline void PGC_REFERENCED_PAGES_MINUS1(PGC *cache, size_t assumed_size)
866 }
867
868 // If the page is not already acquired,
872 -// YOU HAVE TO HAVE THE QUEUE (hot, dirty, clean) THE PAGE IS IN, L O C K E D !
873 -// If you don't have it locked, NOTHING PREVENTS THIS PAGE FOR VANISHING WHILE THIS IS CALLED!
869 +// YOU HAVE TO HAVE THE QUEUE (hot, dirty, clean - the page is in), LOCKED!
870 +// If you don't have it locked, NOTHING PREVENTS THIS PAGE FROM VANISHING WHILE THIS IS CALLED!
871 static inline bool page_acquire(PGC *cache, PGC_PAGE *page) {
872 __atomic_add_fetch(&cache->stats.acquires, 1, __ATOMIC_RELAXED);
873
877 - REFCOUNT expected, desired;
878 -
879 - expected = __atomic_load_n(&page->refcount, __ATOMIC_RELAXED);
880 - size_t spins = 0;
881 -
882 - do {
883 - spins++;
884 -
885 - if(unlikely(expected < 0))
886 - return false;
887 -
888 - desired = expected + 1;
889 -
890 - } while(!__atomic_compare_exchange_n(&page->refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
891 -
892 - if(unlikely(spins > 1))
893 - p2_add_fetch(&cache->stats.p2_waste_acquire_spins, spins - 1);
874 + REFCOUNT rc = refcount_acquire_advanced(&page->refcount);
875 + if(REFCOUNT_ACQUIRED(rc)) {
876 + if(rc == 1)
877 + PGC_REFERENCED_PAGES_PLUS1(cache, page);
878
895 - if(desired == 1)
896 - PGC_REFERENCED_PAGES_PLUS1(cache, page);
879 + return true;
880 + }
881
898 - return true;
882 + return false;
883 }
884
885 static inline void page_release(PGC *cache, PGC_PAGE *page, bool evict_if_necessary) {
886 __atomic_add_fetch(&cache->stats.releases, 1, __ATOMIC_RELAXED);
887
888 size_t assumed_size = page->assumed_size; // take the size before we release it
905 - REFCOUNT expected, desired;
906 -
907 - expected = __atomic_load_n(&page->refcount, __ATOMIC_RELAXED);
908 -
909 - size_t spins = 0;
910 - do {
911 - spins++;
912 -
913 - internal_fatal(expected <= 0,
914 - "DBENGINE CACHE: trying to release a page with reference counter %d", expected);
889
916 - desired = expected - 1;
917 -
918 - } while(!__atomic_compare_exchange_n(&page->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
919 -
920 - if(unlikely(spins > 1))
921 - p2_add_fetch(&cache->stats.p2_waste_release_spins, spins - 1);
922 -
923 - if(desired == 0) {
890 + if(refcount_release(&page->refcount) == 0) {
891 PGC_REFERENCED_PAGES_MINUS1(cache, assumed_size);
892
893 if(evict_if_necessary)
@@ -934,38 +901,17 @@ static inline bool non_acquired_page_get_for_deletion___while_having_clean_locke
901 internal_fatal(!is_page_clean(page),
902 "DBENGINE CACHE: only clean pages can be deleted");
903
937 - REFCOUNT expected, desired;
938 -
939 - expected = __atomic_load_n(&page->refcount, __ATOMIC_RELAXED);
940 - size_t spins = 0;
941 - bool delete_it;
942 -
943 - do {
944 - spins++;
945 -
946 - if (expected == 0) {
947 - desired = REFCOUNT_DICONNECTED;
948 - delete_it = true;
949 - }
950 - else {
951 - delete_it = false;
952 - break;
953 - }
954 -
955 - } while(!__atomic_compare_exchange_n(&page->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
956 -
957 - if(delete_it) {
904 + if(refcount_acquire_for_deletion(&page->refcount)) {
905 // we can delete this page
906 internal_fatal(page_flag_check(page, PGC_PAGE_IS_BEING_DELETED),
907 "DBENGINE CACHE: page is already being deleted");
908
909 page_flag_set(page, PGC_PAGE_IS_BEING_DELETED);
963 - }
910
965 - if(unlikely(spins > 1))
966 - p2_add_fetch(&cache->stats.p2_waste_delete_spins, spins - 1);
911 + return true;
912 + }
913
968 - return delete_it;
914 + return false;
915 }
916
917 static inline bool acquired_page_get_for_deletion_or_release_it(PGC *cache __maybe_unused, PGC_PAGE *page) {
@@ -973,32 +919,7 @@ static inline bool acquired_page_get_for_deletion_or_release_it(PGC *cache __may
919
920 size_t assumed_size = page->assumed_size; // take the size before we release it
921
976 - REFCOUNT expected, desired;
977 -
978 - expected = __atomic_load_n(&page->refcount, __ATOMIC_RELAXED);
979 - size_t spins = 0;
980 - bool delete_it;
981 -
982 - do {
983 - spins++;
984 -
985 - internal_fatal(expected < 1,
986 - "DBENGINE CACHE: page to be deleted should be acquired by the caller.");
987 -
988 - if (expected == 1) {
989 - // we are the only one having this page referenced
990 - desired = REFCOUNT_DICONNECTED;
991 - delete_it = true;
992 - }
993 - else {
994 - // this page cannot be deleted
995 - desired = expected - 1;
996 - delete_it = false;
997 - }
998 -
999 - } while(!__atomic_compare_exchange_n(&page->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
1000 -
1001 - if(delete_it) {
922 + if(refcount_release_and_acquire_for_deletion(&page->refcount)) {
923 PGC_REFERENCED_PAGES_MINUS1(cache, assumed_size);
924
925 // we can delete this page
@@ -1006,12 +927,11 @@ static inline bool acquired_page_get_for_deletion_or_release_it(PGC *cache __may
927 "DBENGINE CACHE: page is already being deleted");
928
929 page_flag_set(page, PGC_PAGE_IS_BEING_DELETED);
1009 - }
930
1011 - if(unlikely(spins > 1))
1012 - p2_add_fetch(&cache->stats.p2_waste_delete_spins, spins - 1);
931 + return true;
932 + }
933
1014 - return delete_it;
934 + return false;
935 }
936
937
@@ -2365,7 +2285,7 @@ bool pgc_flush_pages(PGC *cache) {
2285 }
2286
2287 void pgc_page_hot_set_end_time_s(PGC *cache __maybe_unused, PGC_PAGE *page, time_t end_time_s, size_t additional_bytes) {
2368 - internal_fatal(!is_page_hot(page),
2288 + internal_fatal(!is_page_hot(page) && !netdata_exit,
2289 "DBENGINE CACHE: end_time_s update on non-hot page");
2290
2291 internal_fatal(end_time_s < __atomic_load_n(&page->end_time_s, __ATOMIC_RELAXED),
src/database/engine/cache.h
-3
@@ -144,9 +144,6 @@ struct pgc_statistics {
144 // waste events - spins
145 PAD64(size_t) p2_waste_insert_spins;
146 PAD64(size_t) p2_waste_evict_useless_spins;
147 - PAD64(size_t) p2_waste_release_spins;
148 - PAD64(size_t) p2_waste_acquire_spins;
149 - PAD64(size_t) p2_waste_delete_spins;
147
148 // waste events - eviction
149 PAD64(size_t) p2_waste_evict_relocated;
src/database/engine/journalfile.c
+1 -1
@@ -682,7 +682,7 @@ static void journalfile_restore_extent_metadata(struct rrdengine_instance *ctx,
682 }
683
684 temp_id = (nd_uuid_t *)jf_metric_data->descr[i].uuid;
685 - METRIC *metric = mrg_metric_get_and_acquire(main_mrg, temp_id, (Word_t) ctx);
685 + METRIC *metric = mrg_metric_get_and_acquire_by_uuid(main_mrg, temp_id, (Word_t)ctx);
686
687 struct rrdeng_extent_page_descr *descr = &jf_metric_data->descr[i];
688 VALIDATED_PAGE_DESCRIPTOR vd = validate_extent_page_descr(
src/database/engine/metric.c
+91 -119
@@ -4,20 +4,22 @@
4 #include "libnetdata/locks/locks.h"
5 #include "rrddiskprotocol.h"
6
7 -typedef int32_t REFCOUNT;
8 -#define REFCOUNT_DICONNECTED (-100)
9 -
7 struct metric {
11 - nd_uuid_t uuid; // never changes
8 Word_t section; // never changes
9 + UUIDMAP_ID uuid; // never changes
10 +
11 + REFCOUNT refcount;
12 + uint8_t partition;
13 +
14 + uint32_t latest_update_every_s; // the latest data collection frequency
15
16 time_t first_time_s; // the timestamp of the oldest point in the database
17 time_t latest_time_s_clean; // the timestamp of the newest point in the database
18 time_t latest_time_s_hot; // the timestamp of the latest point that has been collected (not yet stored)
17 - uint32_t latest_update_every_s; // the latest data collection frequency
19 +
20 +#ifdef NETDATA_INTERNAL_CHECKS
21 pid_t writer;
19 - uint8_t partition;
20 - REFCOUNT refcount;
22 +#endif
23
24 // THIS IS allocated with malloc()
25 // YOU HAVE TO INITIALIZE IT YOURSELF !
@@ -45,16 +47,14 @@ struct metric {
47 static struct aral_statistics mrg_aral_statistics;
48
49 struct mrg {
48 - size_t partitions;
49 -
50 struct mrg_partition {
51 ARAL *aral; // not protected by our spinlock - it has its own
52
53 RW_SPINLOCK rw_spinlock;
54 - Pvoid_t uuid_judy; // JudyHS: each UUID has a JudyL of sections (tiers)
54 + Pvoid_t uuid_judy; // JudyL: each UUID has a JudyL of sections (tiers)
55
56 struct mrg_statistics stats;
57 - } index[];
57 + } index[UUIDMAP_PARTITIONS];
58 };
59
60 static inline void MRG_STATS_DUPLICATE_ADD(MRG *mrg, size_t partition) {
@@ -90,27 +90,10 @@ static inline void MRG_STATS_DELETE_MISS(MRG *mrg, size_t partition) {
90 #define mrg_index_write_lock(mrg, partition) rw_spinlock_write_lock(&(mrg)->index[partition].rw_spinlock)
91 #define mrg_index_write_unlock(mrg, partition) rw_spinlock_write_unlock(&(mrg)->index[partition].rw_spinlock)
92
93 -static inline void mrg_stats_size_judyl_change(MRG *mrg, int64_t judy_mem, size_t partition) {
94 - __atomic_add_fetch(&mrg->index[partition].stats.size, judy_mem, __ATOMIC_RELAXED);
95 -}
96 -
97 -static inline void mrg_stats_size_judyhs_added_uuid(MRG *mrg, size_t partition, int64_t judy_mem) {
93 +static inline void mrg_stats_judy_mem(MRG *mrg, size_t partition, int64_t judy_mem) {
94 __atomic_add_fetch(&mrg->index[partition].stats.size, judy_mem, __ATOMIC_RELAXED);
95 }
96
101 -static inline void mrg_stats_size_judyhs_removed_uuid(MRG *mrg, size_t partition, int64_t judy_mem) {
102 - __atomic_sub_fetch(&mrg->index[partition].stats.size, judy_mem, __ATOMIC_RELAXED);
103 -}
104 -
105 -static inline size_t uuid_partition(MRG *mrg __maybe_unused, nd_uuid_t *uuid) {
106 - uint8_t *u = (uint8_t *)uuid;
107 -
108 - size_t n;
109 - memcpy(&n, &u[UUID_SZ - sizeof(size_t)], sizeof(size_t));
110 -
111 - return n % mrg->partitions;
112 -}
113 -
97 static inline time_t mrg_metric_get_first_time_s_smart(MRG *mrg __maybe_unused, METRIC *metric) {
98 time_t first_time_s = __atomic_load_n(&metric->first_time_s, __ATOMIC_RELAXED);
99
@@ -128,26 +111,32 @@ static inline time_t mrg_metric_get_first_time_s_smart(MRG *mrg __maybe_unused,
111 return first_time_s;
112 }
113
131 -static void metric_log(MRG *mrg __maybe_unused, METRIC *metric, const char *msg) {
114 +static inline void metric_log(MRG *mrg __maybe_unused, METRIC *metric, const char *msg) {
115 struct rrdengine_instance *ctx = (struct rrdengine_instance *)metric->section;
116
134 - char uuid[UUID_STR_LEN];
135 - uuid_unparse_lower(metric->uuid, uuid);
117 + nd_uuid_t uuid;
118 + uuidmap_uuid(metric->uuid, uuid);
119 + char uuid_txt[UUID_STR_LEN];
120 + uuid_unparse_lower(uuid, uuid_txt);
121 nd_log(NDLS_DAEMON, NDLP_ERR,
122 "METRIC: %s on %s at tier %d, refcount %d, partition %u, "
138 - "retention [%ld - %ld (hot), %ld (clean)], update every %"PRIu32", "
139 - "writer pid %d "
140 - "--- PLEASE OPEN A GITHUB ISSUE TO REPORT THIS LOG LINE TO NETDATA --- ",
123 + "retention [%ld - %ld (hot), %ld (clean)], update every %"PRIu32
124 +#ifdef NETDATA_INTERNAL_CHECKS
125 + ", writer pid %d "
126 +#endif
127 + " --- PLEASE OPEN A GITHUB ISSUE TO REPORT THIS LOG LINE TO NETDATA --- ",
128 msg,
142 - uuid,
129 + uuid_txt,
130 ctx->config.tier,
131 metric->refcount,
132 metric->partition,
133 metric->first_time_s,
134 metric->latest_time_s_hot,
135 metric->latest_time_s_clean,
149 - metric->latest_update_every_s,
150 - (int)metric->writer
136 + metric->latest_update_every_s
137 +#ifdef NETDATA_INTERNAL_CHECKS
138 + , (int)metric->writer
139 +#endif
140 );
141 }
142
@@ -163,65 +152,53 @@ static inline bool acquired_metric_has_retention(MRG *mrg, METRIC *metric) {
152 }
153
154 static inline void acquired_for_deletion_metric_delete(MRG *mrg, METRIC *metric) {
166 - size_t partition = metric->partition;
155 + JudyAllocThreadPulseReset();
156
168 - int64_t judy_mem;
157 + size_t partition = metric->partition;
158
159 mrg_index_write_lock(mrg, partition);
160
172 - Pvoid_t *sections_judy_pptr = JudyHSGet(mrg->index[partition].uuid_judy, &metric->uuid, sizeof(nd_uuid_t));
161 + Pvoid_t *sections_judy_pptr = JudyLGet(mrg->index[partition].uuid_judy, metric->uuid, PJE0);
162 + if(unlikely(sections_judy_pptr == PJERR))
163 + fatal("METRIC: corrupted JudyL");
164 +
165 if(unlikely(!sections_judy_pptr || !*sections_judy_pptr)) {
166 MRG_STATS_DELETE_MISS(mrg, partition);
167 mrg_index_write_unlock(mrg, partition);
168 return;
169 }
170
179 - judy_mem = -(int64_t)JudyLMemUsed(*sections_judy_pptr);
171 int rc = JudyLDel(sections_judy_pptr, metric->section, PJE0);
181 - judy_mem += (int64_t)JudyLMemUsed(*sections_judy_pptr);
182 - mrg_stats_size_judyl_change(mrg, judy_mem, partition);
183 -
172 if(unlikely(!rc)) {
173 MRG_STATS_DELETE_MISS(mrg, partition);
174 mrg_index_write_unlock(mrg, partition);
175 + mrg_stats_judy_mem(mrg, partition, JudyAllocThreadPulseGetAndReset());
176 return;
177 }
178
179 if(!*sections_judy_pptr) {
191 - JudyAllocThreadPulseReset();
192 -
193 - rc = JudyHSDel(&mrg->index[partition].uuid_judy, &metric->uuid, sizeof(nd_uuid_t), PJE0);
194 -
195 - judy_mem = JudyAllocThreadPulseGetAndReset();
180 + rc = JudyLDel(&mrg->index[partition].uuid_judy, metric->uuid, PJE0);
181
182 if(unlikely(!rc))
198 - fatal("DBENGINE METRIC: cannot delete UUID from JudyHS");
199 - mrg_stats_size_judyhs_removed_uuid(mrg, partition, judy_mem);
183 + fatal("DBENGINE METRIC: cannot delete UUID from JudyL");
184 }
185
186 MRG_STATS_DELETED_METRIC(mrg, partition);
187
188 mrg_index_write_unlock(mrg, partition);
205 -
189 + uuidmap_free(metric->uuid);
190 aral_freez(mrg->index[partition].aral, metric);
191 + mrg_stats_judy_mem(mrg, partition, JudyAllocThreadPulseGetAndReset());
192 }
193
194 static inline bool metric_acquire(MRG *mrg, METRIC *metric) {
210 - REFCOUNT expected, desired;
211 -
212 - expected = __atomic_load_n(&metric->refcount, __ATOMIC_RELAXED);
213 -
214 - do {
215 - if(unlikely(expected < 0))
216 - return false;
217 -
218 - desired = expected + 1;
219 -
220 - } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
195 + REFCOUNT rc = refcount_acquire_advanced(&metric->refcount);
196 + if(!REFCOUNT_ACQUIRED(rc))
197 + return false;
198
199 size_t partition = metric->partition;
200
224 - if(desired == 1)
201 + if(rc == 1)
202 __atomic_add_fetch(&mrg->index[partition].stats.entries_acquired, 1, __ATOMIC_RELAXED);
203
204 __atomic_add_fetch(&mrg->index[partition].stats.current_references, 1, __ATOMIC_RELAXED);
@@ -231,37 +208,30 @@ static inline bool metric_acquire(MRG *mrg, METRIC *metric) {
208
209 static inline bool metric_release(MRG *mrg, METRIC *metric) {
210 size_t partition = metric->partition;
234 - REFCOUNT expected, desired;
235 -
236 - expected = __atomic_load_n(&metric->refcount, __ATOMIC_RELAXED);
237 -
238 - do {
239 - if(expected <= 0) {
240 - metric_log(mrg, metric, "refcount is zero or negative during release");
241 - fatal("METRIC: refcount is %d (zero or negative) during release", expected);
242 - }
211
244 - if(expected == 1 && !acquired_metric_has_retention(mrg, metric))
245 - desired = REFCOUNT_DICONNECTED;
246 - else
247 - desired = expected - 1;
212 + REFCOUNT refcount = refcount_release(&metric->refcount);
213
249 - } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
214 + if(!refcount && !acquired_metric_has_retention(mrg, metric) && refcount_acquire_for_deletion(&metric->refcount))
215 + refcount = REFCOUNT_DELETED;
216
251 - if(desired == 0 || desired == REFCOUNT_DICONNECTED) {
217 + if(refcount == 0 || refcount == REFCOUNT_DELETED) {
218 __atomic_sub_fetch(&mrg->index[partition].stats.entries_acquired, 1, __ATOMIC_RELAXED);
219
254 - if(desired == REFCOUNT_DICONNECTED)
220 + if(refcount == REFCOUNT_DELETED)
221 acquired_for_deletion_metric_delete(mrg, metric);
222 }
223
224 __atomic_sub_fetch(&mrg->index[partition].stats.current_references, 1, __ATOMIC_RELAXED);
225
260 - return desired == REFCOUNT_DICONNECTED;
226 + return refcount == REFCOUNT_DELETED;
227 }
228
229 static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *ret) {
264 - size_t partition = uuid_partition(mrg, entry->uuid);
230 + JudyAllocThreadPulseReset();
231 +
232 + UUIDMAP_ID id = uuidmap_create(*entry->uuid);
233 +
234 + size_t partition = uuid_to_uuidmap_partition(*entry->uuid);
235
236 METRIC *allocation = aral_mallocz(mrg->index[partition].aral);
237 Pvoid_t *PValue;
@@ -269,23 +239,11 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r
239 while(1) {
240 mrg_index_write_lock(mrg, partition);
241
272 - JudyAllocThreadPulseReset();
273 -
274 - Pvoid_t *sections_judy_pptr = JudyHSIns(&mrg->index[partition].uuid_judy, entry->uuid, sizeof(nd_uuid_t), PJE0);
275 -
276 - int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
277 -
242 + Pvoid_t *sections_judy_pptr = JudyLIns(&mrg->index[partition].uuid_judy, id, PJE0);
243 if (unlikely(!sections_judy_pptr || sections_judy_pptr == PJERR))
279 - fatal("DBENGINE METRIC: corrupted UUIDs JudyHS array");
280 -
281 - if (unlikely(!*sections_judy_pptr))
282 - mrg_stats_size_judyhs_added_uuid(mrg, partition, judy_mem);
244 + fatal("DBENGINE METRIC: corrupted UUIDs JudyL array");
245
284 - judy_mem = -(int64_t)JudyLMemUsed(*sections_judy_pptr);
246 PValue = JudyLIns(sections_judy_pptr, entry->section, PJE0);
286 - judy_mem += (int64_t)JudyLMemUsed(*sections_judy_pptr);
287 - mrg_stats_size_judyl_change(mrg, judy_mem, partition);
288 -
247 if (unlikely(!PValue || PValue == PJERR))
248 fatal("DBENGINE METRIC: corrupted section JudyL array");
249
@@ -303,8 +261,10 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r
261 if (ret)
262 *ret = false;
263
264 + uuidmap_free(id);
265 aral_freez(mrg->index[partition].aral, allocation);
266
267 + mrg_stats_judy_mem(mrg, partition, JudyAllocThreadPulseGetAndReset());
268 return metric;
269 }
270
@@ -312,13 +272,15 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r
272 }
273
274 METRIC *metric = allocation;
315 - uuid_copy(metric->uuid, *entry->uuid);
275 + metric->uuid = id;
276 metric->section = entry->section;
277 metric->first_time_s = MAX(0, entry->first_time_s);
278 metric->latest_time_s_clean = MAX(0, entry->last_time_s);
279 metric->latest_time_s_hot = 0;
280 metric->latest_update_every_s = entry->latest_update_every_s;
281 +#ifdef NETDATA_INTERNAL_CHECKS
282 metric->writer = 0;
283 +#endif
284 metric->refcount = 1;
285 metric->partition = partition;
286 *PValue = metric;
@@ -333,16 +295,17 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r
295 if(ret)
296 *ret = true;
297
298 + mrg_stats_judy_mem(mrg, partition, JudyAllocThreadPulseGetAndReset());
299 return metric;
300 }
301
339 -static inline METRIC *metric_get_and_acquire(MRG *mrg, nd_uuid_t *uuid, Word_t section) {
340 - size_t partition = uuid_partition(mrg, uuid);
302 +static inline METRIC *metric_get_and_acquire_by_id(MRG *mrg, UUIDMAP_ID id, Word_t section) {
303 + size_t partition = uuidmap_id_to_partition(id);
304
305 while(1) {
306 mrg_index_read_lock(mrg, partition);
307
345 - Pvoid_t *sections_judy_pptr = JudyHSGet(mrg->index[partition].uuid_judy, uuid, sizeof(nd_uuid_t));
308 + Pvoid_t *sections_judy_pptr = JudyLGet(mrg->index[partition].uuid_judy, id, PJE0);
309 if (unlikely(!sections_judy_pptr)) {
310 mrg_index_read_unlock(mrg, partition);
311 MRG_STATS_SEARCH_MISS(mrg, partition);
@@ -373,14 +336,10 @@ static inline METRIC *metric_get_and_acquire(MRG *mrg, nd_uuid_t *uuid, Word_t s
336 // ----------------------------------------------------------------------------
337 // public API
338
376 -inline MRG *mrg_create(ssize_t partitions) {
377 - if(partitions < 1)
378 - partitions = (ssize_t)netdata_conf_cpus();
339 +inline MRG *mrg_create(void) {
340 + MRG *mrg = callocz(1, sizeof(MRG));
341
380 - MRG *mrg = callocz(1, sizeof(MRG) + sizeof(struct mrg_partition) * partitions);
381 - mrg->partitions = partitions;
382 -
383 - for(size_t i = 0; i < mrg->partitions ; i++) {
342 + for(size_t i = 0; i < _countof(mrg->index) ; i++) {
343 rw_spinlock_init(&mrg->index[i].rw_spinlock);
344
345 char buf[ARAL_MAX_NAME + 1];
@@ -415,8 +374,15 @@ inline METRIC *mrg_metric_add_and_acquire(MRG *mrg, MRG_ENTRY entry, bool *ret)
374 return metric_add_and_acquire(mrg, &entry, ret);
375 }
376
418 -inline METRIC *mrg_metric_get_and_acquire(MRG *mrg, nd_uuid_t *uuid, Word_t section) {
419 - return metric_get_and_acquire(mrg, uuid, section);
377 +inline METRIC *mrg_metric_get_and_acquire_by_uuid(MRG *mrg, nd_uuid_t *uuid, Word_t section) {
378 + UUIDMAP_ID id = uuidmap_create(*uuid);
379 + METRIC *metric = metric_get_and_acquire_by_id(mrg, id, section);
380 + uuidmap_free(id);
381 + return metric;
382 +}
383 +
384 +inline METRIC *mrg_metric_get_and_acquire_by_id(MRG *mrg, UUIDMAP_ID id, Word_t section) {
385 + return metric_get_and_acquire_by_id(mrg, id, section);
386 }
387
388 inline bool mrg_metric_release_and_delete(MRG *mrg, METRIC *metric) {
@@ -437,7 +403,11 @@ inline Word_t mrg_metric_id(MRG *mrg __maybe_unused, METRIC *metric) {
403 }
404
405 inline nd_uuid_t *mrg_metric_uuid(MRG *mrg __maybe_unused, METRIC *metric) {
440 - return &metric->uuid;
406 + return uuidmap_uuid_ptr(metric->uuid);
407 +}
408 +
409 +inline UUIDMAP_ID mrg_metric_uuidmap_id_dup(MRG *mrg __maybe_unused, METRIC *metric) {
410 + return uuidmap_dup(metric->uuid);
411 }
412
413 inline Word_t mrg_metric_section(MRG *mrg __maybe_unused, METRIC *metric) {
@@ -609,6 +579,7 @@ inline uint32_t mrg_metric_get_update_every_s(MRG *mrg __maybe_unused, METRIC *m
579 return __atomic_load_n(&metric->latest_update_every_s, __ATOMIC_RELAXED);
580 }
581
582 +#ifdef NETDATA_INTERNAL_CHECKS
583 inline bool mrg_metric_set_writer(MRG *mrg, METRIC *metric) {
584 pid_t expected = __atomic_load_n(&metric->writer, __ATOMIC_RELAXED);
585 pid_t wanted = gettid_cached();
@@ -648,6 +619,7 @@ inline bool mrg_metric_clear_writer(MRG *mrg, METRIC *metric) {
619
620 return done;
621 }
622 +#endif
623
624 inline void mrg_update_metric_retention_and_granularity_by_uuid(
625 MRG *mrg, Word_t section, nd_uuid_t *uuid,
@@ -682,7 +654,7 @@ inline void mrg_update_metric_retention_and_granularity_by_uuid(
654 }
655
656 bool added = false;
685 - METRIC *metric = mrg_metric_get_and_acquire(mrg, uuid, section);
657 + METRIC *metric = mrg_metric_get_and_acquire_by_uuid(mrg, uuid, section);
658 if (!metric) {
659 MRG_ENTRY entry = {
660 .uuid = uuid,
@@ -724,7 +696,7 @@ inline void mrg_update_metric_retention_and_granularity_by_uuid(
696 inline void mrg_get_statistics(MRG *mrg, struct mrg_statistics *s) {
697 memset(s, 0, sizeof(struct mrg_statistics));
698
727 - for(size_t i = 0; i < mrg->partitions ;i++) {
699 + for(size_t i = 0; i < _countof(mrg->index) ;i++) {
700 s->entries += __atomic_load_n(&mrg->index[i].stats.entries, __ATOMIC_RELAXED);
701 s->entries_acquired += __atomic_load_n(&mrg->index[i].stats.entries_acquired, __ATOMIC_RELAXED);
702 s->size += __atomic_load_n(&mrg->index[i].stats.size, __ATOMIC_RELAXED);
@@ -740,7 +712,7 @@ inline void mrg_get_statistics(MRG *mrg, struct mrg_statistics *s) {
712 s->writers_conflicts += __atomic_load_n(&mrg->index[i].stats.writers_conflicts, __ATOMIC_RELAXED);
713 }
714
743 - s->size += sizeof(MRG) + sizeof(struct mrg_partition) * mrg->partitions;
715 + s->size += sizeof(MRG);
716 }
717
718 // ----------------------------------------------------------------------------
@@ -797,7 +769,7 @@ static void *mrg_stress(void *ptr) {
769 }
770
771 int mrg_unittest(void) {
800 - MRG *mrg = mrg_create(0);
772 + MRG *mrg = mrg_create();
773 METRIC *m1_t0, *m2_t0, *m3_t0, *m4_t0;
774 METRIC *m1_t1, *m2_t1, *m3_t1, *m4_t1;
775 bool ret;
@@ -822,7 +794,7 @@ int mrg_unittest(void) {
794 if(ret)
795 fatal("DBENGINE METRIC: managed to add the same metric twice");
796
825 - m3_t0 = mrg_metric_get_and_acquire(mrg, entry.uuid, entry.section);
797 + m3_t0 = mrg_metric_get_and_acquire_by_uuid(mrg, entry.uuid, entry.section);
798 if(m3_t0 != m1_t0)
799 fatal("DBENGINE METRIC: cannot find the metric added");
800
@@ -846,7 +818,7 @@ int mrg_unittest(void) {
818 if(ret)
819 fatal("DBENGINE METRIC: managed to add the same metric twice in (section 0)");
820
849 - m3_t1 = mrg_metric_get_and_acquire(mrg, entry.uuid, entry.section);
821 + m3_t1 = mrg_metric_get_and_acquire_by_uuid(mrg, entry.uuid, entry.section);
822 if(m3_t1 != m1_t1)
823 fatal("DBENGINE METRIC: cannot find the metric added (section %zu)", (size_t)entry.section);
824
@@ -860,7 +832,7 @@ int mrg_unittest(void) {
832 if(!mrg_metric_release_and_delete(mrg, m1_t0))
833 fatal("DBENGINE METRIC: cannot delete the first metric");
834
863 - m4_t1 = mrg_metric_get_and_acquire(mrg, entry.uuid, entry.section);
835 + m4_t1 = mrg_metric_get_and_acquire_by_uuid(mrg, entry.uuid, entry.section);
836 if(m4_t1 != m1_t1)
837 fatal("DBENGINE METRIC: cannot find the metric added (section %zu), after deleting the first one", (size_t)entry.section);
838
@@ -880,7 +852,7 @@ int mrg_unittest(void) {
852 fatal("DBENGINE METRIC: invalid entries counter");
853
854 size_t entries = 1000000;
883 - size_t threads = mrg->partitions / 3 + 1;
855 + size_t threads = _countof(mrg->index) / 3 + 1;
856 size_t tiers = 3;
857 size_t run_for_secs = 5;
858 netdata_log_info("preparing stress test of %zu entries...", entries);
src/database/engine/metric.h
+6 -2
@@ -40,18 +40,20 @@ struct mrg_statistics {
40 PAD64(size_t) writers_conflicts;
41 };
42
43 -MRG *mrg_create(ssize_t partitions);
43 +MRG *mrg_create(void);
44 void mrg_destroy(MRG *mrg);
45
46 METRIC *mrg_metric_dup(MRG *mrg, METRIC *metric);
47 void mrg_metric_release(MRG *mrg, METRIC *metric);
48
49 METRIC *mrg_metric_add_and_acquire(MRG *mrg, MRG_ENTRY entry, bool *ret);
50 -METRIC *mrg_metric_get_and_acquire(MRG *mrg, nd_uuid_t *uuid, Word_t section);
50 +METRIC *mrg_metric_get_and_acquire_by_id(MRG *mrg, UUIDMAP_ID id, Word_t section);
51 +METRIC *mrg_metric_get_and_acquire_by_uuid(MRG *mrg, nd_uuid_t *uuid, Word_t section);
52 bool mrg_metric_release_and_delete(MRG *mrg, METRIC *metric);
53
54 Word_t mrg_metric_id(MRG *mrg, METRIC *metric);
55 nd_uuid_t *mrg_metric_uuid(MRG *mrg, METRIC *metric);
56 +UUIDMAP_ID mrg_metric_uuidmap_id_dup(MRG *mrg __maybe_unused, METRIC *metric);
57 Word_t mrg_metric_section(MRG *mrg, METRIC *metric);
58
59 bool mrg_metric_set_first_time_s(MRG *mrg, METRIC *metric, time_t first_time_s);
@@ -71,8 +73,10 @@ void mrg_metric_expand_retention(MRG *mrg, METRIC *metric, time_t first_time_s,
73 void mrg_metric_get_retention(MRG *mrg, METRIC *metric, time_t *first_time_s, time_t *last_time_s, uint32_t *update_every_s);
74 bool mrg_metric_zero_disk_retention(MRG *mrg __maybe_unused, METRIC *metric);
75
76 +#ifdef NETDATA_INTERNAL_CHECKS
77 bool mrg_metric_set_writer(MRG *mrg, METRIC *metric);
78 bool mrg_metric_clear_writer(MRG *mrg, METRIC *metric);
79 +#endif
80
81 void mrg_get_statistics(MRG *mrg, struct mrg_statistics *s);
82 struct aral_statistics *mrg_aral_stats(void);
src/database/engine/pagecache.c
+1 -1
@@ -1082,7 +1082,7 @@ size_t pgc_main_nominal_page_size(void *data) {
1082
1083 void pgc_and_mrg_initialize(void)
1084 {
1085 - main_mrg = mrg_create(0);
1085 + main_mrg = mrg_create();
1086
1087 size_t target_cache_size = (size_t)default_rrdeng_page_cache_mb * 1024ULL * 1024ULL;
1088 size_t main_cache_size = (target_cache_size / 100) * 70;
src/database/engine/pdc.c
+1 -1
@@ -1084,7 +1084,7 @@ static bool epdl_populate_pages_from_extent_data(
1084 continue;
1085 }
1086
1087 - METRIC *metric = mrg_metric_get_and_acquire(main_mrg, &header->descr[i].uuid, (Word_t)ctx);
1087 + METRIC *metric = mrg_metric_get_and_acquire_by_uuid(main_mrg, &header->descr[i].uuid, (Word_t)ctx);
1088 Word_t metric_id = (Word_t)metric;
1089 if(!metric) {
1090 char log[200 + 1];
src/database/engine/rrdengine.c
+1 -1
@@ -1129,7 +1129,7 @@ static void update_metrics_first_time_s(struct rrdengine_instance *ctx, struct r
1129
1130 size_t added = 0;
1131 for (size_t index = 0; index < count; ++index) {
1132 - METRIC *metric = mrg_metric_get_and_acquire(main_mrg, &uuid_list[index].uuid, (Word_t) ctx);
1132 + METRIC *metric = mrg_metric_get_and_acquire_by_uuid(main_mrg, &uuid_list[index].uuid, (Word_t)ctx);
1133 if (!metric)
1134 continue;
1135
src/database/engine/rrdengine.h
+5 -1
@@ -170,7 +170,11 @@ struct jv2_page_info {
170 };
171
172 typedef enum __attribute__ ((__packed__)) {
173 - RRDENG_1ST_METRIC_WRITER = (1 << 0),
173 + RRDENG_COLLECT_HANDLE_OPTION_NONE = 0,
174 +
175 +#ifdef NETDATA_INTERNAL_CHECKS
176 + RRDENG_1ST_METRIC_WRITER = (1 << 0),
177 +#endif
178 } RRDENG_COLLECT_HANDLE_OPTIONS;
179
180 typedef enum __attribute__ ((__packed__)) {
src/database/engine/rrdengineapi.c
+45 -14
@@ -126,7 +126,7 @@ static METRIC *rrdeng_metric_unittest(STORAGE_INSTANCE *si, const char *rd_id, c
126 struct rrdengine_instance *ctx = (struct rrdengine_instance *)si;
127 nd_uuid_t legacy_uuid;
128 rrdeng_generate_unittest_uuid(rd_id, st_id, &legacy_uuid);
129 - return mrg_metric_get_and_acquire(main_mrg, &legacy_uuid, (Word_t) ctx);
129 + return mrg_metric_get_and_acquire_by_uuid(main_mrg, &legacy_uuid, (Word_t)ctx);
130 }
131
132 // ----------------------------------------------------------------------------
@@ -142,9 +142,14 @@ STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *smh) {
142 return (STORAGE_METRIC_HANDLE *) mrg_metric_dup(main_mrg, metric);
143 }
144
145 -STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *si, nd_uuid_t *uuid) {
145 +STORAGE_METRIC_HANDLE *rrdeng_metric_get_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *uuid) {
146 struct rrdengine_instance *ctx = (struct rrdengine_instance *)si;
147 - return (STORAGE_METRIC_HANDLE *) mrg_metric_get_and_acquire(main_mrg, uuid, (Word_t) ctx);
147 + return (STORAGE_METRIC_HANDLE *)mrg_metric_get_and_acquire_by_uuid(main_mrg, uuid, (Word_t)ctx);
148 +}
149 +
150 +STORAGE_METRIC_HANDLE *rrdeng_metric_get_by_id(STORAGE_INSTANCE *si, UUIDMAP_ID id) {
151 + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si;
152 + return (STORAGE_METRIC_HANDLE *)mrg_metric_get_and_acquire_by_id(main_mrg, id, (Word_t)ctx);
153 }
154
155 static METRIC *rrdeng_metric_create(STORAGE_INSTANCE *si, nd_uuid_t *uuid) {
@@ -170,25 +175,25 @@ STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE
175 struct rrdengine_instance *ctx = (struct rrdengine_instance *)si;
176 METRIC *metric;
177
173 - metric = mrg_metric_get_and_acquire(main_mrg, &rd->metric_uuid, (Word_t) ctx);
178 + metric = mrg_metric_get_and_acquire_by_id(main_mrg, rd->uuid, (Word_t) ctx);
179
180 if(unlikely(!metric)) {
181 if(unlikely(unittest_running)) {
182 metric = rrdeng_metric_unittest(si, rrddim_id(rd), rrdset_id(rd->rrdset));
183 if (metric)
179 - uuid_copy(rd->metric_uuid, *mrg_metric_uuid(main_mrg, metric));
184 + rd->uuid = mrg_metric_uuidmap_id_dup(main_mrg, metric);
185 }
186
187 if(likely(!metric))
183 - metric = rrdeng_metric_create(si, &rd->metric_uuid);
188 + metric = rrdeng_metric_create(si, uuidmap_uuid_ptr(rd->uuid));
189 }
190
191 #ifdef NETDATA_INTERNAL_CHECKS
187 - if(!uuid_eq(rd->metric_uuid, *mrg_metric_uuid(main_mrg, metric))) {
192 + if(!uuid_eq(*uuidmap_uuid_ptr(rd->uuid), *mrg_metric_uuid(main_mrg, metric))) {
193 char uuid1[UUID_STR_LEN + 1];
194 char uuid2[UUID_STR_LEN + 1];
195
191 - uuid_unparse(rd->metric_uuid, uuid1);
196 + uuid_unparse(*uuidmap_uuid_ptr(rd->uuid), uuid1);
197 uuid_unparse(*mrg_metric_uuid(main_mrg, metric), uuid2);
198 fatal("DBENGINE: uuids do not match, asked for metric '%s', but got metric '%s'", uuid1, uuid2);
199 }
@@ -263,6 +268,8 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *smh, uin
268 METRIC *metric = (METRIC *)smh;
269 struct rrdengine_instance *ctx = mrg_metric_ctx(metric);
270
271 + RRDENG_COLLECT_HANDLE_OPTIONS options = 0;
272 +#ifdef NETDATA_INTERNAL_CHECKS
273 bool is_1st_metric_writer = true;
274 if(!mrg_metric_set_writer(main_mrg, metric)) {
275 is_1st_metric_writer = false;
@@ -270,6 +277,12 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *smh, uin
277 uuid_unparse(*mrg_metric_uuid(main_mrg, metric), uuid);
278 netdata_log_error("DBENGINE: metric '%s' is already collected and should not be collected twice - expect gaps on the charts", uuid);
279 }
280 + if(is_1st_metric_writer)
281 + options = RRDENG_1ST_METRIC_WRITER;
282 + else
283 + __atomic_add_fetch(&ctx->atomic.collectors_running_duplicate, 1, __ATOMIC_RELAXED);
284 +
285 +#endif
286
287 metric = mrg_metric_dup(main_mrg, metric);
288
@@ -285,11 +298,9 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *smh, uin
298 handle->page_position = 0;
299 handle->page_entries_max = 0;
300 handle->update_every_ut = (usec_t)update_every * USEC_PER_SEC;
288 - handle->options = is_1st_metric_writer ? RRDENG_1ST_METRIC_WRITER : 0;
301 + handle->options = options;
302
303 __atomic_add_fetch(&ctx->atomic.collectors_running, 1, __ATOMIC_RELAXED);
291 - if(!is_1st_metric_writer)
292 - __atomic_add_fetch(&ctx->atomic.collectors_running_duplicate, 1, __ATOMIC_RELAXED);
304
305 mrg_metric_set_update_every(main_mrg, metric, update_every);
306
@@ -654,11 +665,14 @@ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *sch) {
665 rrdeng_page_alignment_release(handle->alignment);
666
667 __atomic_sub_fetch(&ctx->atomic.collectors_running, 1, __ATOMIC_RELAXED);
668 +
669 +#ifdef NETDATA_INTERNAL_CHECKS
670 if(!(handle->options & RRDENG_1ST_METRIC_WRITER))
671 __atomic_sub_fetch(&ctx->atomic.collectors_running_duplicate, 1, __ATOMIC_RELAXED);
672
673 if((handle->options & RRDENG_1ST_METRIC_WRITER) && !mrg_metric_clear_writer(main_mrg, handle->metric))
674 internal_fatal(true, "DBENGINE: metric is already released");
675 +#endif
676
677 time_t first_time_s, last_time_s;
678 mrg_metric_get_retention(main_mrg, handle->metric, &first_time_s, &last_time_s, NULL);
@@ -953,15 +967,32 @@ time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *smh) {
967 return oldest_time_s;
968 }
969
956 -bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s)
957 -{
970 +bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s) {
971 + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si;
972 + if (unlikely(!ctx)) {
973 + netdata_log_error("DBENGINE: invalid STORAGE INSTANCE to %s()", __FUNCTION__);
974 + return false;
975 + }
976 +
977 + METRIC *metric = mrg_metric_get_and_acquire_by_uuid(main_mrg, dim_uuid, (Word_t)ctx);
978 + if (unlikely(!metric))
979 + return false;
980 +
981 + mrg_metric_get_retention(main_mrg, metric, first_entry_s, last_entry_s, NULL);
982 +
983 + mrg_metric_release(main_mrg, metric);
984 +
985 + return true;
986 +}
987 +
988 +bool rrdeng_metric_retention_by_id(STORAGE_INSTANCE *si, UUIDMAP_ID id, time_t *first_entry_s, time_t *last_entry_s) {
989 struct rrdengine_instance *ctx = (struct rrdengine_instance *)si;
990 if (unlikely(!ctx)) {
991 netdata_log_error("DBENGINE: invalid STORAGE INSTANCE to %s()", __FUNCTION__);
992 return false;
993 }
994
964 - METRIC *metric = mrg_metric_get_and_acquire(main_mrg, dim_uuid, (Word_t) ctx);
995 + METRIC *metric = mrg_metric_get_and_acquire_by_id(main_mrg, id, (Word_t)ctx);
996 if (unlikely(!metric))
997 return false;
998
src/database/engine/rrdengineapi.h
+4 -1
@@ -34,7 +34,8 @@ extern uint8_t tier_page_type[];
34 #define CTX_POINT_SIZE_BYTES(ctx) page_type_size[(ctx)->config.page_type]
35
36 STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si);
37 -STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
37 +STORAGE_METRIC_HANDLE *rrdeng_metric_get_by_id(STORAGE_INSTANCE *si, UUIDMAP_ID id);
38 +STORAGE_METRIC_HANDLE *rrdeng_metric_get_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
39 void rrdeng_metric_release(STORAGE_METRIC_HANDLE *smh);
40 STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *smh);
41
@@ -74,6 +75,8 @@ void rrdeng_readiness_wait(struct rrdengine_instance *ctx);
75
76 int rrdeng_exit(struct rrdengine_instance *ctx);
77 void rrdeng_quiesce(struct rrdengine_instance *ctx);
78 +
79 +bool rrdeng_metric_retention_by_id(STORAGE_INSTANCE *si, UUIDMAP_ID id, time_t *first_entry_s, time_t *last_entry_s);
80 bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s);
81
82 extern STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
src/database/ram/rrddim_mem.c
+60 -36
@@ -3,8 +3,8 @@
3 #include "rrddim_mem.h"
4 #include "Judy.h"
5
6 -static Pvoid_t rrddim_JudyHS_array = NULL;
7 -static netdata_rwlock_t rrddim_JudyHS_rwlock = NETDATA_RWLOCK_INITIALIZER;
6 +static Pvoid_t rrddim_Judy_array = NULL;
7 +static netdata_rwlock_t rrddim_Judy_rwlock = NETDATA_RWLOCK_INITIALIZER;
8
9 // ----------------------------------------------------------------------------
10 // metrics groups
@@ -30,7 +30,7 @@ struct mem_metric_handle {
30 time_t last_updated_s;
31 time_t update_every_s;
32
33 - int32_t refcount;
33 + REFCOUNT refcount;
34 };
35
36 static void update_metric_handle_from_rrddim(struct mem_metric_handle *mh, RRDDIM *rd) {
@@ -47,13 +47,12 @@ static void check_metric_handle_from_rrddim(struct mem_metric_handle *mh) {
47 internal_fatal(mh->update_every_s != rd->rrdset->update_every, "RRDDIM: update every does not match");
48 }
49
50 -STORAGE_METRIC_HANDLE *
51 -rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si __maybe_unused) {
52 - struct mem_metric_handle *mh = (struct mem_metric_handle *)rrddim_metric_get(si, &rd->metric_uuid);
50 +STORAGE_METRIC_HANDLE *rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si) {
51 + struct mem_metric_handle *mh = (struct mem_metric_handle *)rrddim_metric_get_by_id(si, rd->uuid);
52 while(!mh) {
54 - netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock);
53 + netdata_rwlock_wrlock(&rrddim_Judy_rwlock);
54 JudyAllocThreadPulseReset();
56 - Pvoid_t *PValue = JudyHSIns(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(nd_uuid_t), PJE0);
55 + Pvoid_t *PValue = JudyLIns(&rrddim_Judy_array, rd->uuid, PJE0);
56 int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
57 mh = *PValue;
58 if(!mh) {
@@ -65,63 +64,88 @@ rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si __maybe_unused) {
64 pulse_db_rrd_memory_change(judy_mem + (int64_t)sizeof(struct mem_metric_handle));
65 }
66 else {
68 - if(__atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) <= 0)
67 + if(!refcount_acquire(&mh->refcount))
68 mh = NULL;
69 }
71 - netdata_rwlock_wrunlock(&rrddim_JudyHS_rwlock);
70 + netdata_rwlock_wrunlock(&rrddim_Judy_rwlock);
71 }
72
74 - internal_fatal(mh->rd != rd, "RRDDIM_MEM: incorrect pointer returned from index.");
73 + if(unlikely(mh->rd != rd))
74 + fatal("DB_RAM_ALLOC: incorrect pointer returned from index.");
75
76 return (STORAGE_METRIC_HANDLE *)mh;
77 }
78
79 -STORAGE_METRIC_HANDLE *
80 -rrddim_metric_get(STORAGE_INSTANCE *si __maybe_unused, nd_uuid_t *uuid) {
79 +STORAGE_METRIC_HANDLE *rrddim_metric_get_by_id(STORAGE_INSTANCE *si __maybe_unused, UUIDMAP_ID id) {
80 struct mem_metric_handle *mh = NULL;
82 - netdata_rwlock_rdlock(&rrddim_JudyHS_rwlock);
83 - Pvoid_t *PValue = JudyHSGet(rrddim_JudyHS_array, uuid, sizeof(nd_uuid_t));
84 - if (likely(NULL != PValue)) {
85 - mh = *PValue;
86 - if(__atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) <= 0)
87 - mh = NULL;
81 +
82 + netdata_rwlock_rdlock(&rrddim_Judy_rwlock);
83 + {
84 + Pvoid_t *PValue = JudyLGet(rrddim_Judy_array, id, PJE0);
85 + if (unlikely(PValue == PJERR))
86 + fatal("DB_RAM_ALLOC: corrupted judy array!");
87 +
88 + if (likely(NULL != PValue)) {
89 + mh = *PValue;
90 + if (!refcount_acquire(&mh->refcount))
91 + mh = NULL;
92 + }
93 }
89 - netdata_rwlock_rdunlock(&rrddim_JudyHS_rwlock);
94 + netdata_rwlock_rdunlock(&rrddim_Judy_rwlock);
95
96 return (STORAGE_METRIC_HANDLE *)mh;
97 }
98
99 +STORAGE_METRIC_HANDLE *rrddim_metric_get_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *uuid) {
100 + UUIDMAP_ID id = uuidmap_create(*uuid);
101 + STORAGE_METRIC_HANDLE *mh = rrddim_metric_get_by_id(si, id);
102 + uuidmap_free(id);
103 + return mh;
104 +}
105 +
106 STORAGE_METRIC_HANDLE *rrddim_metric_dup(STORAGE_METRIC_HANDLE *smh) {
107 struct mem_metric_handle *mh = (struct mem_metric_handle *)smh;
96 - __atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED);
108 +
109 + if(!refcount_acquire(&mh->refcount))
110 + fatal("DB_RAM_ALLOC: cannot acquire an already acquired refcount");
111 +
112 return smh;
113 }
114
115 void rrddim_metric_release(STORAGE_METRIC_HANDLE *smh __maybe_unused) {
116 struct mem_metric_handle *mh = (struct mem_metric_handle *)smh;
117
103 - if(__atomic_sub_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) == 0) {
104 - // we are the last one holding this
118 + if(refcount_release_and_acquire_for_deletion(&mh->refcount)) {
119 + // we can delete it
120
106 - int32_t expected = 0;
107 - if(__atomic_compare_exchange_n(&mh->refcount, &expected, -99999, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
108 - // we can delete it
109 -
110 - RRDDIM *rd = mh->rd;
111 - netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock);
121 + int64_t judy_mem = 0;
122 + RRDDIM *rd = mh->rd;
123 + netdata_rwlock_wrlock(&rrddim_Judy_rwlock);
124 + {
125 JudyAllocThreadPulseReset();
113 - JudyHSDel(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(nd_uuid_t), PJE0);
114 - int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
115 - netdata_rwlock_wrunlock(&rrddim_JudyHS_rwlock);
116 -
117 - freez(mh);
118 - pulse_db_rrd_memory_change(judy_mem - (int64_t)sizeof(struct mem_metric_handle));
126 + JudyLDel(&rrddim_Judy_array, rd->uuid, PJE0);
127 + judy_mem = JudyAllocThreadPulseGetAndReset();
128 }
129 + netdata_rwlock_wrunlock(&rrddim_Judy_rwlock);
130 +
131 + freez(mh);
132 + pulse_db_rrd_memory_change(judy_mem - (int64_t)sizeof(struct mem_metric_handle));
133 }
134 }
135
136 bool rrddim_metric_retention_by_uuid(STORAGE_INSTANCE *si __maybe_unused, nd_uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s) {
124 - STORAGE_METRIC_HANDLE *smh = rrddim_metric_get(si, uuid);
137 + STORAGE_METRIC_HANDLE *smh = rrddim_metric_get_by_uuid(si, uuid);
138 + if(!smh)
139 + return false;
140 +
141 + *first_entry_s = rrddim_query_oldest_time_s(smh);
142 + *last_entry_s = rrddim_query_latest_time_s(smh);
143 +
144 + return true;
145 +}
146 +
147 +bool rrddim_metric_retention_by_id(STORAGE_INSTANCE *si __maybe_unused, UUIDMAP_ID id, time_t *first_entry_s, time_t *last_entry_s) {
148 + STORAGE_METRIC_HANDLE *smh = rrddim_metric_get_by_id(si, id);
149 if(!smh)
150 return false;
151
src/database/ram/rrddim_mem.h
+3 -1
@@ -23,10 +23,12 @@ struct mem_query_handle {
23 };
24
25 STORAGE_METRIC_HANDLE *rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si);
26 -STORAGE_METRIC_HANDLE *rrddim_metric_get(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
26 +STORAGE_METRIC_HANDLE *rrddim_metric_get_by_id(STORAGE_INSTANCE *si, UUIDMAP_ID id);
27 +STORAGE_METRIC_HANDLE *rrddim_metric_get_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
28 STORAGE_METRIC_HANDLE *rrddim_metric_dup(STORAGE_METRIC_HANDLE *smh);
29 void rrddim_metric_release(STORAGE_METRIC_HANDLE *smh);
30
31 +bool rrddim_metric_retention_by_id(STORAGE_INSTANCE *si, UUIDMAP_ID id, time_t *first_entry_s, time_t *last_entry_s);
32 bool rrddim_metric_retention_by_uuid(STORAGE_INSTANCE *si, nd_uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s);
33
34 STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
src/database/rrd.h
+4 -2
@@ -289,7 +289,7 @@ bool backfill_tier_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s);
289 // RRD DIMENSION - this is a metric
290
291 struct rrddim {
292 - nd_uuid_t metric_uuid; // global UUID for this metric (unique_across hosts)
292 + UUIDMAP_ID uuid;
293
294 // ------------------------------------------------------------------------
295 // dimension definition
@@ -654,10 +654,12 @@ static inline time_t storage_engine_align_to_optimal_before(struct storage_engin
654 // function pointers for all APIs provided by a storage engine
655 typedef struct storage_engine_api {
656 // metric management
657 - STORAGE_METRIC_HANDLE *(*metric_get)(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
657 + STORAGE_METRIC_HANDLE *(*metric_get_by_id)(STORAGE_INSTANCE *si, UUIDMAP_ID id);
658 + STORAGE_METRIC_HANDLE *(*metric_get_by_uuid)(STORAGE_INSTANCE *si, nd_uuid_t *uuid);
659 STORAGE_METRIC_HANDLE *(*metric_get_or_create)(RRDDIM *rd, STORAGE_INSTANCE *si);
660 void (*metric_release)(STORAGE_METRIC_HANDLE *);
661 STORAGE_METRIC_HANDLE *(*metric_dup)(STORAGE_METRIC_HANDLE *);
662 + bool (*metric_retention_by_id)(STORAGE_INSTANCE *si, UUIDMAP_ID id, time_t *first_entry_s, time_t *last_entry_s);
663 bool (*metric_retention_by_uuid)(STORAGE_INSTANCE *si, nd_uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s);
664 } STORAGE_ENGINE_API;
665
src/database/rrdcollector.c
+11 -47
@@ -11,8 +11,8 @@
11 // rrdset_collector_finished()).
12
13 struct rrd_collector {
14 - int32_t refcount;
15 - int32_t refcount_dispatcher;
14 + REFCOUNT refcount;
15 + REFCOUNT refcount_dispatcher;
16 pid_t tid;
17 bool running;
18 };
@@ -32,32 +32,18 @@ inline pid_t rrd_collector_tid(struct rrd_collector *rdc) {
32 }
33
34 bool rrd_collector_dispatcher_acquire(struct rrd_collector *rdc) {
35 - int32_t expected = __atomic_load_n(&rdc->refcount_dispatcher, __ATOMIC_RELAXED);
36 - int32_t wanted;
37 - do {
38 - if(expected < 0)
39 - return false;
40 -
41 - wanted = expected + 1;
42 - } while(!__atomic_compare_exchange_n(&rdc->refcount_dispatcher, &expected, wanted, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
43 -
44 - return true;
35 + return refcount_acquire(&rdc->refcount_dispatcher);
36 }
37
38 void rrd_collector_dispatcher_release(struct rrd_collector *rdc) {
48 - __atomic_sub_fetch(&rdc->refcount_dispatcher, 1, __ATOMIC_RELAXED);
39 + refcount_release(&rdc->refcount_dispatcher);
40 }
41
42 static void rrd_collector_free(struct rrd_collector *rdc) {
52 - if(rdc->running)
53 - return;
54 -
55 - int32_t expected = 0;
56 - if(!__atomic_compare_exchange_n(&rdc->refcount, &expected, -1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
43 + if(rrd_collector_running(rdc) || !refcount_acquire_for_deletion(&rdc->refcount))
44 // the collector is still referenced by charts.
45 // leave it hanging there, the last chart will actually free it.
46 return;
60 - }
47
48 // we can free it now
49 freez(rdc);
@@ -84,27 +70,18 @@ void rrd_collector_finished(void) {
70 // so, while cancellation requests are being dispatched, this structure is accessed.
71 // delaying the exit of the thread is required to avoid cleaning up this structure.
72
87 - int32_t expected = 0;
88 - while(!__atomic_compare_exchange_n(&thread_rrd_collector->refcount_dispatcher, &expected, -1, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
89 - expected = 0;
73 + while(!refcount_acquire_for_deletion(&thread_rrd_collector->refcount_dispatcher))
74 sleep_usec(1 * USEC_PER_MS);
91 - }
75
76 rrd_collector_free(thread_rrd_collector);
77 thread_rrd_collector = NULL;
78 }
79
97 -bool rrd_collector_acquire(struct rrd_collector *rdc) {
98 -
99 - int32_t expected = __atomic_load_n(&rdc->refcount, __ATOMIC_RELAXED), wanted = 0;
100 - do {
101 - if(expected < 0 || !rrd_collector_running(rdc))
102 - return false;
80 +static bool rrd_collector_acquire(struct rrd_collector *rdc) {
81 + if(!rdc || !rrd_collector_running(rdc))
82 + return false;
83
104 - wanted = expected + 1;
105 - } while(!__atomic_compare_exchange_n(&rdc->refcount, &expected, wanted, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
106 -
107 - return true;
84 + return refcount_acquire(&rdc->refcount);
85 }
86
87 struct rrd_collector *rrd_collector_acquire_current_thread(void) {
@@ -119,19 +96,6 @@ struct rrd_collector *rrd_collector_acquire_current_thread(void) {
96 void rrd_collector_release(struct rrd_collector *rdc) {
97 if(unlikely(!rdc)) return;
98
122 - int32_t expected = __atomic_load_n(&rdc->refcount, __ATOMIC_RELAXED), wanted = 0;
123 - do {
124 - if(expected < 0)
125 - return;
126 -
127 - if(expected == 0) {
128 - internal_fatal(true, "FUNCTIONS: Trying to release a collector that is not acquired.");
129 - return;
130 - }
131 -
132 - wanted = expected - 1;
133 - } while(!__atomic_compare_exchange_n(&rdc->refcount, &expected, wanted, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
134 -
135 - if(wanted == 0)
99 + if(refcount_release(&rdc->refcount) == 0)
100 rrd_collector_free(rdc);
101 }
src/database/rrddim.c
+6 -3
@@ -84,8 +84,10 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
84
85 rd->rrd_memory_mode = ctr->memory_mode;
86
87 - if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &(rd->metric_uuid))))
88 - uuid_generate(rd->metric_uuid);
87 + nd_uuid_t uuid;
88 + if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &uuid)))
89 + uuid_generate(uuid);
90 + rd->uuid = uuidmap_create(uuid);
91
92 // initialize the db tiers
93 {
@@ -209,7 +211,7 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
211
212 if (!rrddim_finalize_collection_and_check_retention(rd) && rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
213 /* This metric has no data and no references */
212 - metaqueue_delete_dimension_uuid(&rd->metric_uuid);
214 + metaqueue_delete_dimension_uuid(uuidmap_uuid_ptr(rd->uuid));
215 }
216
217 for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
@@ -233,6 +235,7 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
235
236 string_freez(rd->id);
237 string_freez(rd->name);
238 + uuidmap_free(rd->uuid);
239 }
240
241 static bool rrddim_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *new_rrddim, void *constructor_data) {
src/database/sqlite/sqlite_metadata.c
+2 -1
@@ -1061,7 +1061,8 @@ static int store_dimension_metadata(RRDDIM *rd)
1061 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_STORE_DIMENSION, &res))
1062 return 1;
1063
1064 - SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &rd->metric_uuid, sizeof(rd->metric_uuid), SQLITE_STATIC));
1064 + nd_uuid_t *rd_uuid = uuidmap_uuid_ptr(rd->uuid);
1065 + SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, rd_uuid, sizeof(*rd_uuid), SQLITE_STATIC));
1066 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &rd->rrdset->chart_uuid, sizeof(rd->rrdset->chart_uuid), SQLITE_STATIC));
1067 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_text(res, ++param, string2str(rd->id), -1, SQLITE_STATIC));
1068 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_text(res, ++param, string2str(rd->name), -1, SQLITE_STATIC));
src/database/storage_engine.c
+12 -4
@@ -12,10 +12,12 @@ static STORAGE_ENGINE engines[] = {
12 .name = RRD_MEMORY_MODE_NONE_NAME,
13 .seb = STORAGE_ENGINE_BACKEND_RRDDIM,
14 .api = {
15 - .metric_get = rrddim_metric_get,
15 + .metric_get_by_id = rrddim_metric_get_by_id,
16 + .metric_get_by_uuid = rrddim_metric_get_by_uuid,
17 .metric_get_or_create = rrddim_metric_get_or_create,
18 .metric_dup = rrddim_metric_dup,
19 .metric_release = rrddim_metric_release,
20 + .metric_retention_by_id = rrddim_metric_retention_by_id,
21 .metric_retention_by_uuid = rrddim_metric_retention_by_uuid,
22 }
23 },
@@ -24,10 +26,12 @@ static STORAGE_ENGINE engines[] = {
26 .name = RRD_MEMORY_MODE_RAM_NAME,
27 .seb = STORAGE_ENGINE_BACKEND_RRDDIM,
28 .api = {
27 - .metric_get = rrddim_metric_get,
29 + .metric_get_by_id = rrddim_metric_get_by_id,
30 + .metric_get_by_uuid = rrddim_metric_get_by_uuid,
31 .metric_get_or_create = rrddim_metric_get_or_create,
32 .metric_dup = rrddim_metric_dup,
33 .metric_release = rrddim_metric_release,
34 + .metric_retention_by_id = rrddim_metric_retention_by_id,
35 .metric_retention_by_uuid = rrddim_metric_retention_by_uuid,
36 }
37 },
@@ -36,10 +40,12 @@ static STORAGE_ENGINE engines[] = {
40 .name = RRD_MEMORY_MODE_ALLOC_NAME,
41 .seb = STORAGE_ENGINE_BACKEND_RRDDIM,
42 .api = {
39 - .metric_get = rrddim_metric_get,
43 + .metric_get_by_id = rrddim_metric_get_by_id,
44 + .metric_get_by_uuid = rrddim_metric_get_by_uuid,
45 .metric_get_or_create = rrddim_metric_get_or_create,
46 .metric_dup = rrddim_metric_dup,
47 .metric_release = rrddim_metric_release,
48 + .metric_retention_by_id = rrddim_metric_retention_by_id,
49 .metric_retention_by_uuid = rrddim_metric_retention_by_uuid,
50 }
51 },
@@ -49,10 +55,12 @@ static STORAGE_ENGINE engines[] = {
55 .name = RRD_MEMORY_MODE_DBENGINE_NAME,
56 .seb = STORAGE_ENGINE_BACKEND_DBENGINE,
57 .api = {
52 - .metric_get = rrdeng_metric_get,
58 + .metric_get_by_id = rrdeng_metric_get_by_id,
59 + .metric_get_by_uuid = rrdeng_metric_get_by_uuid,
60 .metric_get_or_create = rrdeng_metric_get_or_create,
61 .metric_dup = rrdeng_metric_dup,
62 .metric_release = rrdeng_metric_release,
63 + .metric_retention_by_id = rrdeng_metric_retention_by_id,
64 .metric_retention_by_uuid = rrdeng_metric_retention_by_uuid,
65 }
66 },
src/health/health_event_loop.c
+1 -1
@@ -105,7 +105,7 @@ static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run)
105 return 1;
106 }
107
108 -static void health_sleep(time_t next_run, uint64_t loop) {
108 +static void health_sleep(time_t next_run, uint64_t loop __maybe_unused) {
109 time_t now = now_realtime_sec();
110 if(now < next_run) {
111 worker_is_idle();
src/libnetdata/atomics/atomics.h
+1
@@ -4,5 +4,6 @@
4 #define NETDATA_ATOMICS_H
5
6 #include "atomic_flags.h"
7 +#include "refcount.h"
8
9 #endif //NETDATA_ATOMICS_H
src/libnetdata/atomics/refcount.h new
+129
@@ -0,0 +1,129 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_REFCOUNT_H
4 +#define NETDATA_REFCOUNT_H
5 +
6 +#include "libnetdata/common.h"
7 +#include <inttypes.h>
8 +
9 +typedef int32_t REFCOUNT;
10 +
11 +// the max number of references supported
12 +// we use this to prevent overflowing the reference counter
13 +#define REFCOUNT_MAX (1 * 1000 * 1000 * 1000)
14 +
15 +// We set REFCOUNT_DELETED to a big negative,
16 +// but to a value we can easily recognize while debugging.
17 +#define REFCOUNT_DELETED (-2 * 1000 * 1000 * 1000)
18 +
19 +// The error is a negative number, so that refcount > 0 is still
20 +// good for checking if an acquired succeeded
21 +#define REFCOUNT_ERROR INT32_MIN
22 +
23 +/*
24 + * When debugging:
25 + *
26 + * 1. refcount 0 to 1 billion => the object is referenced
27 + * 2. refcount -1 billion to -1 => double releases or corruption
28 + * 2. refcount -2 billion to -1 billion => marked for deletion, with active references
29 + * (this happens when you use refcount_acquire_for_deletion_and_wait())
30 + * 4. refcount outside -2 billion to 1 billion => memory corruption
31 + */
32 +
33 +#define refcount_references(refcount) __atomic_load_n(refcount, __ATOMIC_RELAXED)
34 +#define refcount_increment(refcount) __atomic_add_fetch(refcount, 1, __ATOMIC_ACQUIRE)
35 +#define refcount_decrement(refcount) __atomic_sub_fetch(refcount, 1, __ATOMIC_RELEASE)
36 +#define REFCOUNT_ACQUIRED(refcount) (refcount > 0)
37 +
38 +#define REFCOUNT_VALID(refcount) \
39 + (((refcount) >= 0 && (refcount) <= REFCOUNT_MAX) || \
40 + ((refcount) >= REFCOUNT_DELETED && (refcount) <= -REFCOUNT_MAX))
41 +
42 +// returns the non-usable refcount found when it fails, the final refcount when it succeeds
43 +static inline REFCOUNT WARNUNUSED refcount_acquire_advanced_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
44 + REFCOUNT expected = refcount_references(refcount);
45 + REFCOUNT desired;
46 +
47 + do {
48 + if(!REFCOUNT_VALID(expected))
49 + fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
50 +
51 + if(expected >= REFCOUNT_MAX)
52 + return REFCOUNT_ERROR;
53 +
54 + if(expected < 0)
55 + return expected;
56 +
57 + desired = expected + 1;
58 + } while(!__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
59 +
60 + return desired;
61 +}
62 +
63 +static inline bool WARNUNUSED refcount_acquire_with_trace(REFCOUNT *refcount, const char *func) {
64 + return REFCOUNT_ACQUIRED(refcount_acquire_advanced_with_trace(refcount, func));
65 +}
66 +
67 +// returns the number of references remaining
68 +static inline REFCOUNT refcount_release_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
69 + REFCOUNT rc = refcount_decrement(refcount);
70 +
71 + if(!REFCOUNT_VALID(rc))
72 + fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", rc, __FUNCTION__, func);
73 +
74 + return rc;
75 +}
76 +
77 +// returns true when the item can be deleted, false when the item is currently referenced
78 +static inline bool WARNUNUSED refcount_acquire_for_deletion_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
79 + REFCOUNT expected = 0;
80 + REFCOUNT desired = REFCOUNT_DELETED;
81 +
82 + if(__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
83 + return true;
84 +
85 + if(!REFCOUNT_VALID(expected))
86 + fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
87 +
88 + return false;
89 +}
90 +
91 +static inline bool WARNUNUSED refcount_release_and_acquire_for_deletion_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
92 + if(refcount_release_with_trace(refcount, func) == 0)
93 + return refcount_acquire_for_deletion_with_trace(refcount, func);
94 +
95 + return false;
96 +}
97 +
98 +// this sleeps for 1 nanosecond (posix systems), or Sleep(0) on Windows
99 +void tinysleep(void);
100 +
101 +static inline bool refcount_acquire_for_deletion_and_wait_with_trace(REFCOUNT *refcount, const char *func) {
102 + REFCOUNT expected = refcount_references(refcount);
103 + REFCOUNT desired;
104 +
105 + do {
106 + if(!REFCOUNT_VALID(expected))
107 + fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
108 +
109 + if(expected < 0)
110 + return false;
111 +
112 + desired = REFCOUNT_DELETED + expected;
113 + } while(!__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
114 +
115 + while(__atomic_load_n(refcount, __ATOMIC_ACQUIRE) != REFCOUNT_DELETED) {
116 + tinysleep();
117 + }
118 +
119 + return true;
120 +}
121 +
122 +#define refcount_acquire_advanced(refcount) refcount_acquire_advanced_with_trace(refcount, __FUNCTION__ )
123 +#define refcount_acquire(refcount) refcount_acquire_with_trace(refcount, __FUNCTION__)
124 +#define refcount_release(refcount) refcount_release_with_trace(refcount, __FUNCTION__)
125 +#define refcount_acquire_for_deletion(refcount) refcount_acquire_for_deletion_with_trace(refcount, __FUNCTION__)
126 +#define refcount_release_and_acquire_for_deletion(refcount) refcount_release_and_acquire_for_deletion_with_trace(refcount, __FUNCTION__)
127 +#define refcount_acquire_for_deletion_and_wait(refcount) refcount_acquire_for_deletion_and_wait_with_trace(refcount, __FUNCTION__)
128 +
129 +#endif //NETDATA_REFCOUNT_H
src/libnetdata/common.h
+2 -6
@@ -259,7 +259,7 @@ typedef uint32_t uid_t;
259 #endif
260
261 #define XXH_INLINE_ALL
262 -#include "xxHash/xxhash.h"
262 +#include "libnetdata/xxHash/xxhash.h"
263
264 // --------------------------------------------------------------------------------------------------------------------
265 // OpenSSL
@@ -385,7 +385,7 @@ typedef uint32_t uid_t;
385 // --------------------------------------------------------------------------------------------------------------------
386 // Macro-only includes
387
388 -#include "linked_lists/linked_lists.h"
388 +#include "libnetdata/linked_lists/linked_lists.h"
389
390 // --------------------------------------------------------------------------------------------------------------------
391
@@ -400,10 +400,6 @@ typedef uint32_t uid_t;
400
401 // --------------------------------------------------------------------------------------------------------------------
402
403 -typedef int32_t REFCOUNT;
404 -
405 -// --------------------------------------------------------------------------------------------------------------------
406 -
403 #if defined(OS_WINDOWS)
404 #include <windows.h>
405 #include <wctype.h>
src/libnetdata/dictionary/dictionary-internals.h
-4
@@ -47,8 +47,6 @@ typedef enum __attribute__ ((__packed__)) item_flags {
47 #define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_RELAXED)
48 #define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_RELAXED)
49
50 -#define REFCOUNT_DICONNECTED (-100)
51 -
50 #define ITEM_FLAGS_TYPE uint8_t
51 #define KEY_LEN_TYPE uint32_t
52 #define VALUE_LEN_TYPE uint32_t
@@ -65,8 +63,6 @@ typedef enum __attribute__ ((__packed__)) item_flags {
63 * Every item in the dictionary has the following structure.
64 */
65
68 -typedef int32_t REFCOUNT;
69 -
66 typedef struct dictionary_item_shared {
67 void *value; // the value of the dictionary item
68
src/libnetdata/dictionary/dictionary-refcount.h
+2 -2
@@ -189,7 +189,7 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
189 // if we can set refcount to REFCOUNT_DELETING, we can delete this item
190
191 size_t spins = 0;
192 - REFCOUNT refcount, desired = REFCOUNT_DICONNECTED;
192 + REFCOUNT refcount, desired = REFCOUNT_DELETED;
193
194 int ret = RC_ITEM_OK;
195
@@ -234,7 +234,7 @@ static inline bool item_shared_release_and_check_if_it_can_be_freed(DICTIONARY *
234 // if we can set refcount to REFCOUNT_DELETING, we can delete this item
235
236 REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_RELEASE);
237 - if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DICONNECTED, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
237 + if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETED, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
238
239 // we can delete it
240 return true;
src/libnetdata/libnetdata.h
+3 -1
@@ -8,13 +8,14 @@ extern "C" {
8 # endif
9
10 #include "common.h"
11 +#include "log/nd_log-fatal.h"
12 +#include "atomics/atomics.h"
13
14 // NETDATA_TRACE_ALLOCATIONS does not work under musl libc, so don't enable it
15 //#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
16 //#define NETDATA_TRACE_ALLOCATIONS 1
17 //#endif
18
17 -#include "atomics/atomics.h"
19 #include "libjudy/judy-malloc.h"
20 #include "locks/benchmark.h"
21 #include "locks/benchmark-rw.h"
@@ -116,6 +117,7 @@ extern const char *netdata_configured_host_prefix;
117 #include "buffer/buffer.h"
118
119 #include "uuid/uuid.h"
120 +#include "uuid/uuidmap.h"
121 #include "http/content_type.h"
122 #include "http/http_access.h"
123
src/libnetdata/log/nd_log-fatal.h new
+19
@@ -0,0 +1,19 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ND_LOG_FATAL_H
4 +#define NETDATA_ND_LOG_FATAL_H
5 +
6 +#include "libnetdata/common.h"
7 +
8 +void netdata_logger_fatal( const char *file, const char *function, unsigned long line, const char *fmt, ... ) NORETURN PRINTFLIKE(4, 5);
9 +
10 +#define fatal(args...) netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, ##args)
11 +#define fatal_assert(expr) ((expr) ? (void)(0) : netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, "Assertion `%s' failed", #expr))
12 +
13 +#ifdef NETDATA_INTERNAL_CHECKS
14 +#define internal_fatal(condition, args...) do { if(unlikely(condition)) netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
15 +#else
16 +#define internal_fatal(args...) debug_dummy()
17 +#endif
18 +
19 +#endif //NETDATA_ND_LOG_FATAL_H
src/libnetdata/log/nd_log.h
+1 -6
@@ -9,6 +9,7 @@ extern "C" {
9
10 #include "../libnetdata.h"
11 #include "nd_log-common.h"
12 +#include "nd_log-fatal.h"
13
14 #define ND_LOG_DEFAULT_THROTTLE_LOGS 1000
15 #define ND_LOG_DEFAULT_THROTTLE_PERIOD 60
@@ -124,16 +125,11 @@ void nd_log_limits_unlimited(void);
125 #ifdef NETDATA_INTERNAL_CHECKS
126 #define netdata_log_debug(type, args...) do { if(unlikely(debug_flags & type)) netdata_logger(NDLS_DEBUG, NDLP_DEBUG, __FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
127 #define internal_error(condition, args...) do { if(unlikely(condition)) netdata_logger(NDLS_DAEMON, NDLP_DEBUG, __FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
127 -#define internal_fatal(condition, args...) do { if(unlikely(condition)) netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
128 #else
129 #define netdata_log_debug(type, args...) debug_dummy()
130 #define internal_error(args...) debug_dummy()
131 -#define internal_fatal(args...) debug_dummy()
131 #endif
132
134 -#define fatal(args...) netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, ##args)
135 -#define fatal_assert(expr) ((expr) ? (void)(0) : netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, "Assertion `%s' failed", #expr))
136 -
133 // ----------------------------------------------------------------------------
134 // normal logging
135
@@ -170,7 +166,6 @@ void netdata_logger_with_limit(ERROR_LIMIT *erl, ND_LOG_SOURCES source, ND_LOG_F
166
167 // ----------------------------------------------------------------------------
168
173 -void netdata_logger_fatal( const char *file, const char *function, unsigned long line, const char *fmt, ... ) NORETURN PRINTFLIKE(4, 5);
169
170 #define error_report(x, args...) do { errno_clear(); netdata_log_error(x, ##args); } while(0)
171
src/libnetdata/string/string.c
+5 -43
@@ -3,8 +3,6 @@
3 #include "../libnetdata.h"
4 #include <Judy.h>
5
6 -typedef int32_t REFCOUNT;
7 -
6 // ----------------------------------------------------------------------------
7 // STRING implementation - dedup all STRING
8
@@ -91,34 +89,13 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_
89 }
90 }
91
94 -#define string_entry_acquire(se) __atomic_add_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST)
95 -#define string_entry_release(se) __atomic_sub_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST)
96 -
92 static inline bool string_entry_check_and_acquire(STRING *se) {
93 #ifdef NETDATA_INTERNAL_CHECKS
94 uint8_t partition = string_partition(se);
95 #endif
96
102 - REFCOUNT expected, desired, count = 0;
103 -
104 - expected = __atomic_load_n(&se->refcount, __ATOMIC_SEQ_CST);
105 -
106 - do {
107 - count++;
108 -
109 - if(expected <= 0) {
110 - // We cannot use this.
111 - // The reference counter reached value zero,
112 - // so another thread is deleting this.
113 - string_internal_stats_add(partition, spins, count - 1);
114 - return false;
115 - }
116 -
117 - desired = expected + 1;
118 -
119 - } while(!__atomic_compare_exchange_n(&se->refcount, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
120 -
121 - string_internal_stats_add(partition, spins, count - 1);
97 + if(!refcount_acquire(&se->refcount))
98 + return false;
99
100 // statistics
101 // string_base.active_references is altered at the in string_strdupz() and string_freez()
@@ -130,12 +107,8 @@ static inline bool string_entry_check_and_acquire(STRING *se) {
107 STRING *string_dup(STRING *string) {
108 if(unlikely(!string)) return NULL;
109
133 -#ifdef NETDATA_INTERNAL_CHECKS
134 - if(unlikely(__atomic_load_n(&string->refcount, __ATOMIC_SEQ_CST) <= 0))
135 - fatal("STRING: tried to %s() a string that is freed (it has %d references).", __FUNCTION__, string->refcount);
136 -#endif
137 -
138 - string_entry_acquire(string);
110 + if(!refcount_acquire(&string->refcount))
111 + fatal("STRING: tried to %s() a string that is deleted (refcount %d).", __FUNCTION__, string->refcount);
112
113 #ifdef NETDATA_INTERNAL_CHECKS
114 uint8_t partition = string_partition(string);
@@ -262,11 +235,6 @@ static inline void string_index_delete(STRING *string) {
235
236 rw_spinlock_write_lock(&string_base[partition].spinlock);
237
265 -#ifdef NETDATA_INTERNAL_CHECKS
266 - if(unlikely(__atomic_load_n(&string->refcount, __ATOMIC_SEQ_CST) != 0))
267 - fatal("STRING: tried to delete a string at %s() that is already freed (it has %d references).", __FUNCTION__, string->refcount);
268 -#endif
269 -
238 bool deleted = false;
239 int64_t judy_mem = 0;
240
@@ -352,14 +320,8 @@ void string_freez(STRING *string) {
320 #ifdef NETDATA_INTERNAL_CHECKS
321 uint8_t partition = string_partition(string);
322 #endif
355 - REFCOUNT refcount = string_entry_release(string);
356 -
357 -#ifdef NETDATA_INTERNAL_CHECKS
358 - if(unlikely(refcount < 0))
359 - fatal("STRING: tried to %s() a string that is already freed (it has %d references).", __FUNCTION__, string->refcount);
360 -#endif
323
362 - if(unlikely(refcount == 0))
324 + if(unlikely(refcount_release_and_acquire_for_deletion(&string->refcount)))
325 string_index_delete(string);
326
327 // statistics
src/libnetdata/uuid/uuidmap.c new
+664
@@ -0,0 +1,664 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "uuidmap.h"
4 +
5 +#define UUIDMAP_REUSE_GAP 1000
6 +
7 +struct uuidmap_entry {
8 + nd_uuid_t uuid;
9 + REFCOUNT refcount;
10 +};
11 +
12 +struct uuidmap_partition {
13 + Pvoid_t uuid_to_id; // JudyL: UUID string -> ID
14 + Pvoid_t id_to_uuid; // JudyL: ID -> UUID binary
15 + Pvoid_t freed_ids; // JudyL: the freed IDs
16 + UUIDMAP_ID next_id; // Only use lower bits
17 + UUIDMAP_ID next_free_id; // fifo id when reusing freed ids
18 + RW_SPINLOCK spinlock;
19 +
20 + int64_t memory;
21 + int32_t entries;
22 +};
23 +
24 +static struct {
25 + struct uuidmap_partition p[UUIDMAP_PARTITIONS];
26 + ARAL *ar;
27 +} uuid_map = { 0 };
28 +
29 +static struct aral_statistics uuidmap_stats = { 0 };
30 +struct aral_statistics *uuidmap_aral_statistics(void) { return &uuidmap_stats; }
31 +
32 +size_t uuidmap_memory(void) {
33 + size_t memory = 0;
34 +
35 + for(size_t i = 0; i < _countof(uuid_map.p) ;i++) {
36 + rw_spinlock_read_lock(&uuid_map.p[i].spinlock);
37 + memory += uuid_map.p[i].memory;
38 + rw_spinlock_read_unlock(&uuid_map.p[i].spinlock);
39 + }
40 +
41 + return memory;
42 +}
43 +
44 +size_t uuidmap_free_bytes(void) {
45 + return aral_free_bytes_from_stats(&uuidmap_stats);
46 +}
47 +
48 +static void uuidmap_init_aral(void) {
49 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
50 +
51 + if(!uuid_map.ar) {
52 + spinlock_lock(&spinlock);
53 + if(!uuid_map.ar) {
54 + uuid_map.ar = aral_create(
55 + "uuidmap",
56 + sizeof(struct uuidmap_entry),
57 + 0,
58 + 0,
59 + &uuidmap_stats,
60 + NULL, NULL, false, false, true);
61 + }
62 + spinlock_unlock(&spinlock);
63 + }
64 +}
65 +
66 +static UUIDMAP_ID get_next_id_unsafe(struct uuidmap_partition *partition) {
67 + UUIDMAP_ID id = 0;
68 +
69 + // Try to get a freed ID first if we have enough gap
70 + Pvoid_t *PValue;
71 + Word_t Index = 0;
72 +
73 + PValue = JudyLFirst(partition->freed_ids, &Index, PJE0);
74 + if (PValue && PValue != PJERR && *PValue) {
75 + // Check if the stored ID is old enough to be reused
76 + UUIDMAP_ID stored_id = *(UUIDMAP_ID *)PValue;
77 + UUIDMAP_ID current_next_id = uuidmap_make_id(partition - uuid_map.p, partition->next_id);
78 +
79 + if ((current_next_id - stored_id) >= UUIDMAP_REUSE_GAP) {
80 + id = stored_id;
81 + // Remove this entry from freed_ids since we're reusing it
82 + int rc = JudyLDel(&partition->freed_ids, Index, PJE0);
83 + if (unlikely(!rc))
84 + fatal("UUIDMAP: cannot delete ID from freed_ids JudyL");
85 + }
86 + }
87 +
88 + if (id == 0) {
89 + // No reusable IDs available, get next sequential ID
90 + id = uuidmap_make_id(partition - uuid_map.p, ++partition->next_id);
91 + }
92 +
93 + return id;
94 +}
95 +
96 +static inline UUIDMAP_ID uuidmap_acquire_by_uuid(const nd_uuid_t uuid) {
97 + UUIDMAP_ID id = 0;
98 +
99 + uint8_t partition = uuid_to_uuidmap_partition(uuid);
100 +
101 + // try to find it in the JudyHS - we may have it already
102 + rw_spinlock_read_lock(&uuid_map.p[partition].spinlock);
103 + Pvoid_t *PValue = JudyHSGet(uuid_map.p[partition].uuid_to_id, (void *)uuid, sizeof(nd_uuid_t));
104 + if(PValue == PJERR)
105 + fatal("UUIDMAP: corrupted JudyHS array");
106 +
107 + if(PValue && *PValue) {
108 + // it is found
109 +
110 + id = *(UUIDMAP_ID *)PValue;
111 +
112 + PValue = JudyLGet(uuid_map.p[partition].id_to_uuid, id, PJE0);
113 + if (!PValue || PValue == PJERR)
114 + fatal("UUIDMAP: corrupted JudyL array");
115 +
116 + struct uuidmap_entry *ue = *PValue;
117 + if(!refcount_acquire(&ue->refcount))
118 + id = 0;
119 + }
120 +
121 + rw_spinlock_read_unlock(&uuid_map.p[partition].spinlock);
122 + return id;
123 +}
124 +
125 +UUIDMAP_ID uuidmap_create(const nd_uuid_t uuid) {
126 + UUIDMAP_ID id = uuidmap_acquire_by_uuid(uuid);
127 + if(id != 0) return id;
128 +
129 + uuidmap_init_aral();
130 +
131 + // we didn't find it - let's add it
132 +
133 + uint8_t partition = uuid_to_uuidmap_partition(uuid);
134 +
135 + JudyAllocThreadPulseReset();
136 +
137 + Pvoid_t *PValue;
138 + while(true) {
139 + rw_spinlock_write_lock(&uuid_map.p[partition].spinlock);
140 +
141 + PValue = JudyHSIns(&uuid_map.p[partition].uuid_to_id, (void *)uuid, sizeof(nd_uuid_t), PJE0);
142 + if (!PValue || PValue == PJERR)
143 + fatal("UUIDMAP: corrupted JudyHS array");
144 +
145 + // If value exists, return it
146 + if (*PValue != 0) {
147 + id = (UUIDMAP_ID)(uintptr_t)*PValue;
148 +
149 + PValue = JudyLGet(uuid_map.p[partition].id_to_uuid, id, PJE0);
150 + if (!PValue || PValue == PJERR)
151 + fatal("UUIDMAP: corrupted JudyL array");
152 +
153 + struct uuidmap_entry *ue = *PValue;
154 + if (!refcount_acquire(&ue->refcount)) {
155 + rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
156 + continue;
157 + }
158 +
159 + uuid_map.p[partition].memory += JudyAllocThreadPulseGetAndReset();
160 + rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
161 + return id;
162 + }
163 + else
164 + break;
165 + }
166 +
167 + id = get_next_id_unsafe(&uuid_map.p[partition]);
168 + *(UUIDMAP_ID *)PValue = id;
169 +
170 + // Store ID -> UUID mapping
171 + PValue = JudyLIns(&uuid_map.p[partition].id_to_uuid, id, PJE0);
172 + if (!PValue || PValue == PJERR)
173 + fatal("UUIDMAP: corrupted JudyL array");
174 +
175 + struct uuidmap_entry *ue = aral_mallocz(uuid_map.ar);
176 + nd_uuid_copy(ue->uuid, uuid);
177 + ue->refcount = 1;
178 + *PValue = ue;
179 +
180 + uuid_map.p[partition].entries++;
181 + uuid_map.p[partition].memory += sizeof(*ue);
182 +
183 + uuid_map.p[partition].memory += JudyAllocThreadPulseGetAndReset();
184 + rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
185 + return id;
186 +}
187 +
188 +static struct uuidmap_entry *get_entry_by_id(UUIDMAP_ID id) {
189 + if(id == 0) return NULL;
190 +
191 + uint8_t partition = uuidmap_id_to_partition(id);
192 +
193 + rw_spinlock_read_lock(&uuid_map.p[partition].spinlock);
194 +
195 + Pvoid_t *PValue = JudyLGet(uuid_map.p[partition].id_to_uuid, id, PJE0);
196 + if (PValue == PJERR)
197 + fatal("UUIDMAP: corrupted JudyL array");
198 +
199 + struct uuidmap_entry *ue = PValue ? *PValue : NULL;
200 +
201 + rw_spinlock_read_unlock(&uuid_map.p[partition].spinlock);
202 +
203 + return ue;
204 +}
205 +
206 +void uuidmap_free(UUIDMAP_ID id) {
207 + struct uuidmap_entry *ue = get_entry_by_id(id);
208 +
209 + if(ue && refcount_release_and_acquire_for_deletion(&ue->refcount)) {
210 + JudyAllocThreadPulseReset();
211 + uint8_t partition = uuidmap_id_to_partition(id);
212 + rw_spinlock_write_lock(&uuid_map.p[partition].spinlock);
213 +
214 + int rc;
215 + rc = JudyHSDel(&uuid_map.p[partition].uuid_to_id, (void *)ue->uuid, sizeof(nd_uuid_t), PJE0);
216 + if(unlikely(!rc))
217 + fatal("UUIDMAP: cannot delete UUID from JudyHS");
218 +
219 + rc = JudyLDel(&uuid_map.p[partition].id_to_uuid, id, PJE0);
220 + if(unlikely(!rc))
221 + fatal("UUIDMAP: cannot delete ID from JudyL");
222 +
223 + // Add the freed ID to the freed_ids JudyL using next_free_id as index
224 + Pvoid_t *PValue = JudyLIns(&uuid_map.p[partition].freed_ids, ++uuid_map.p[partition].next_free_id, PJE0);
225 + if (!PValue || PValue == PJERR)
226 + fatal("UUIDMAP: corrupted freed_ids JudyL array");
227 +
228 + *(UUIDMAP_ID *)PValue = id; // Store the actual METRIC_ID as the value
229 +
230 + uuid_map.p[partition].memory -= sizeof(*ue);
231 + uuid_map.p[partition].entries--;
232 +
233 + uuid_map.p[partition].memory += JudyAllocThreadPulseGetAndReset();
234 + rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
235 +
236 + aral_freez(uuid_map.ar, ue);
237 + }
238 +}
239 +
240 +nd_uuid_t *uuidmap_uuid_ptr(UUIDMAP_ID id) {
241 + struct uuidmap_entry *ue = get_entry_by_id(id);
242 + return ue ? &ue->uuid : NULL;
243 +}
244 +
245 +nd_uuid_t *uuidmap_uuid_ptr_and_dup(UUIDMAP_ID id) {
246 + struct uuidmap_entry *ue = get_entry_by_id(id);
247 +
248 + if(ue && refcount_acquire(&ue->refcount))
249 + return &ue->uuid;
250 +
251 + return NULL;
252 +}
253 +
254 +bool uuidmap_uuid(UUIDMAP_ID id, nd_uuid_t out_uuid) {
255 + nd_uuid_t *uuid = uuidmap_uuid_ptr(id);
256 +
257 + if(!uuid) {
258 + nd_uuid_clear(out_uuid);
259 + return false;
260 + }
261 +
262 + uuid_copy(out_uuid, *uuid);
263 + return true;
264 +}
265 +
266 +ND_UUID uuidmap_get(UUIDMAP_ID id) {
267 + ND_UUID uuid;
268 + uuidmap_uuid(id, uuid.uuid);
269 + return uuid;
270 +}
271 +
272 +UUIDMAP_ID uuidmap_dup(UUIDMAP_ID id) {
273 + struct uuidmap_entry *ue = get_entry_by_id(id);
274 +
275 + if(!ue || !refcount_acquire(&ue->refcount))
276 + fatal("UUIDMAP: id %u does not exist, or cannot be acquired, in %s", id, __FUNCTION__ );
277 +
278 + return id;
279 +}
280 +
281 +// --------------------------------------------------------------------------------------------------------------------
282 +
283 +static volatile bool stop_flag = false;
284 +
285 +typedef struct thread_stats {
286 + size_t creates;
287 + size_t finds;
288 + size_t dups;
289 + size_t frees;
290 + size_t cycles;
291 +} THREAD_STATS;
292 +
293 +static void *concurrent_test_thread(void *arg) {
294 + THREAD_STATS *stats = arg;
295 + nd_uuid_t test_uuid = {
296 + 0x12, 0x34, 0x56, 0x78,
297 + 0x9a, 0xbc, 0xde, 0xf0,
298 + 0x12, 0x34, 0x56, 0x78,
299 + 0x9a, 0xbc, 0xde, 0xf0
300 + };
301 +
302 + while(!__atomic_load_n(&stop_flag, __ATOMIC_RELAXED)) {
303 + // 1. Create UUID (refcount 1)
304 + UUIDMAP_ID id = uuidmap_create(test_uuid);
305 + if(!id) continue;
306 + stats->creates++;
307 +
308 + // 2. Find its pointer
309 + nd_uuid_t *uuid_ptr = uuidmap_uuid_ptr(id);
310 + if(!uuid_ptr) {
311 + fprintf(stderr, "ERROR: Cannot find UUID we just created\n");
312 + break;
313 + }
314 + stats->finds++;
315 +
316 + // 3. Dup it (refcount 2)
317 + UUIDMAP_ID id2 = uuidmap_dup(id);
318 + if(!id2) {
319 + fprintf(stderr, "ERROR: Cannot dup UUID\n");
320 + break;
321 + }
322 + stats->dups++;
323 +
324 + // 4. Free it once (refcount 1)
325 + uuidmap_free(id);
326 + stats->frees++;
327 +
328 + // 5. Find its pointer again
329 + uuid_ptr = uuidmap_uuid_ptr(id2);
330 + if(!uuid_ptr) {
331 + fprintf(stderr, "ERROR: Cannot find UUID after first free\n");
332 + break;
333 + }
334 + stats->finds++;
335 +
336 + // 6. Free it twice (should delete)
337 + uuidmap_free(id2);
338 + stats->frees++;
339 +
340 + stats->cycles++;
341 + }
342 +
343 + return NULL;
344 +}
345 +
346 +static int uuidmap_concurrent_unittest(void) {
347 + const int num_threads = 4;
348 + const int num_seconds = 5;
349 + fprintf(stderr, "\nTesting concurrent UUID Map access with %d threads for %d seconds...\n", num_threads, num_seconds);
350 + int errors = 0;
351 +
352 + THREAD_STATS stats[num_threads];
353 + memset(stats, 0, sizeof(stats));
354 +
355 + ND_THREAD *threads[num_threads];
356 +
357 + // Start threads
358 + __atomic_store_n(&stop_flag, false, __ATOMIC_RELAXED);
359 +
360 + for(int i = 0; i < num_threads; i++) {
361 + char thread_name[32];
362 + snprintf(thread_name, sizeof(thread_name), "UUID-TEST-%d", i);
363 + threads[i] = nd_thread_create(
364 + thread_name,
365 + NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE,
366 + concurrent_test_thread,
367 + &stats[i]);
368 + }
369 +
370 + // Let it run for 5 seconds
371 + sleep_usec(num_seconds * USEC_PER_SEC);
372 +
373 + // Stop threads
374 + __atomic_store_n(&stop_flag, true, __ATOMIC_RELEASE);
375 +
376 + // Wait for threads
377 + for(int i = 0; i < num_threads; i++)
378 + nd_thread_join(threads[i]);
379 +
380 + // Print statistics
381 + size_t total_cycles = 0;
382 + for(int i = 0; i < num_threads; i++) {
383 + fprintf(stderr, "Thread %d stats:\n"
384 + " Cycles completed : %zu\n"
385 + " Creates : %zu\n"
386 + " Finds : %zu\n"
387 + " Dups : %zu\n"
388 + " Frees : %zu\n",
389 + i,
390 + stats[i].cycles,
391 + stats[i].creates,
392 + stats[i].finds,
393 + stats[i].dups,
394 + stats[i].frees);
395 +
396 + total_cycles += stats[i].cycles;
397 + }
398 +
399 + fprintf(stderr, "\nTotal cycles completed: %zu (%.2f cycles/sec)\n",
400 + total_cycles,
401 + (double)total_cycles / 5.0);
402 +
403 + return errors;
404 +}
405 +
406 +int uuidmap_unittest(void) {
407 + fprintf(stderr, "\nTesting UUID Map...\n");
408 +
409 + const size_t ENTRIES = 100000;
410 + int errors = uuidmap_concurrent_unittest();
411 +
412 + struct test_entry {
413 + nd_uuid_t uuid;
414 + UUIDMAP_ID id;
415 + };
416 +
417 + struct test_entry *entries = mallocz(sizeof(struct test_entry) * ENTRIES);
418 +
419 + fprintf(stderr, "Generating and testing %zu entries...\n", ENTRIES);
420 +
421 + usec_t start_time = now_monotonic_usec();
422 + size_t step = ENTRIES / 100;
423 + size_t next_step = step;
424 +
425 + for(size_t i = 0; i < ENTRIES; i++) {
426 + if (i >= next_step) {
427 + fprintf(stderr, ".");
428 + next_step += step;
429 + }
430 +
431 + uuid_generate_random(entries[i].uuid);
432 + char uuid_str[UUID_STR_LEN];
433 + uuid_unparse_lower(entries[i].uuid, uuid_str);
434 +
435 + // Test 1: Should not exist yet
436 + UUIDMAP_ID id = uuidmap_acquire_by_uuid(entries[i].uuid);
437 + if(id != 0) {
438 + fprintf(stderr, "\nERROR [%zu]: UUID found before adding it"
439 + "\n UUID: %s"
440 + "\n Got ID: %u (expected: 0)\n",
441 + i, uuid_str, id);
442 + errors++;
443 + }
444 +
445 + // Test 2: Create it
446 + id = uuidmap_create(entries[i].uuid);
447 + if(id == 0) {
448 + fprintf(stderr, "\nERROR [%zu]: Failed to create UUID mapping"
449 + "\n UUID: %s\n",
450 + i, uuid_str);
451 + errors++;
452 + continue;
453 + }
454 +
455 + // Test 3: Create again, should return same id
456 + UUIDMAP_ID id2 = uuidmap_create(entries[i].uuid);
457 + if(id2 != id) {
458 + fprintf(stderr, "\nERROR [%zu]: Second create returned different ID"
459 + "\n UUID: %s"
460 + "\n First ID: %u"
461 + "\n Second ID: %u\n",
462 + i, uuid_str, id, id2);
463 + errors++;
464 + }
465 +
466 + // Test 4: Get UUID and verify
467 + nd_uuid_t test_uuid;
468 + if(!uuidmap_uuid(id, test_uuid)) {
469 + fprintf(stderr, "\nERROR [%zu]: Failed to get UUID for valid ID"
470 + "\n UUID: %s"
471 + "\n ID: %u\n",
472 + i, uuid_str, id);
473 + errors++;
474 + }
475 + else {
476 + char test_uuid_str[UUID_STR_LEN];
477 + uuid_unparse_lower(test_uuid, test_uuid_str);
478 + if(uuid_compare(test_uuid, entries[i].uuid) != 0) {
479 + fprintf(stderr, "\nERROR [%zu]: Retrieved UUID doesn't match original"
480 + "\n Original UUID: %s"
481 + "\n Retrieved UUID: %s"
482 + "\n ID: %u\n",
483 + i, uuid_str, test_uuid_str, id);
484 + errors++;
485 + }
486 + }
487 +
488 + // Test 5: Free once (decrease refcount)
489 + uuidmap_free(id);
490 +
491 + // Test 6: Should still exist
492 + if(!uuidmap_uuid(id, test_uuid)) {
493 + fprintf(stderr, "\nERROR [%zu]: UUID disappeared after first free"
494 + "\n UUID: %s"
495 + "\n ID: %u\n",
496 + i, uuid_str, id);
497 + errors++;
498 + }
499 + else {
500 + char test_uuid_str[UUID_STR_LEN];
501 + uuid_unparse_lower(test_uuid, test_uuid_str);
502 + if(uuid_compare(test_uuid, entries[i].uuid) != 0) {
503 + fprintf(stderr, "\nERROR [%zu]: Retrieved UUID doesn't match after first free"
504 + "\n Original UUID: %s"
505 + "\n Retrieved UUID: %s"
506 + "\n ID: %u\n",
507 + i, uuid_str, test_uuid_str, id);
508 + errors++;
509 + }
510 + }
511 +
512 + // Test 7: Free again (should delete)
513 + uuidmap_free(id);
514 +
515 + // Test 8: Should be gone
516 + if(uuidmap_uuid_ptr(id) != NULL) {
517 + char curr_uuid_str[UUID_STR_LEN];
518 + nd_uuid_t *curr_uuid = uuidmap_uuid_ptr(id);
519 + if(curr_uuid)
520 + uuid_unparse_lower(*curr_uuid, curr_uuid_str);
521 +
522 + fprintf(stderr, "\nERROR [%zu]: UUID still exists after second free"
523 + "\n Original UUID: %s"
524 + "\n Current UUID: %s"
525 + "\n ID: %u\n",
526 + i, uuid_str, curr_uuid_str, id);
527 +
528 + errors++;
529 + }
530 +
531 + // Test 9: Create again for phase 2
532 + id = uuidmap_create(entries[i].uuid);
533 + if(id == 0) {
534 + fprintf(stderr, "\nERROR [%zu]: Failed to recreate UUID mapping"
535 + "\n UUID: %s\n",
536 + i, uuid_str);
537 + errors++;
538 + continue;
539 + }
540 +
541 + entries[i].id = id;
542 + }
543 +
544 + usec_t end_time = now_monotonic_usec();
545 + fprintf(stderr, "\nPhase 1 completed in %.2f seconds with %d errors\n",
546 + (double)(end_time - start_time) / (double)USEC_PER_SEC, errors);
547 +
548 + // BENCHMARK while we have all entries loaded
549 + if(errors == 0) {
550 + fprintf(stderr, "\nBenchmarking UUID retrievals...\n");
551 +
552 + // First benchmark: uuidmap_uuid_ptr()
553 + size_t successful = 0;
554 + usec_t start_ut = now_monotonic_usec();
555 +
556 + for(size_t i = 0; i < ENTRIES; i++) {
557 + nd_uuid_t *uuid_ptr = uuidmap_uuid_ptr(entries[i].id);
558 + if(uuid_ptr && uuid_compare(*uuid_ptr, entries[i].uuid) == 0)
559 + successful++;
560 + }
561 +
562 + usec_t end_ut = now_monotonic_usec();
563 + double secs = (double)(end_ut - start_ut) / USEC_PER_SEC;
564 + double ops = (double)successful / secs;
565 +
566 + fprintf(stderr, "uuidmap_uuid_ptr() : %.2f ops/sec (%.2f usec/op)\n",
567 + ops, (double)(end_ut - start_ut) / successful);
568 +
569 + // Second benchmark: uuidmap_get_by_uuid()
570 + successful = 0;
571 + start_ut = now_monotonic_usec();
572 +
573 + for(size_t i = 0; i < ENTRIES; i++) {
574 + UUIDMAP_ID id = uuidmap_acquire_by_uuid(entries[i].uuid);
575 + if(id != 0) {
576 + successful++;
577 + uuidmap_free(id); // Must free since get_by_uuid increases refcount
578 + }
579 + }
580 +
581 + end_ut = now_monotonic_usec();
582 + secs = (double)(end_ut - start_ut) / USEC_PER_SEC;
583 + ops = (double)successful / secs;
584 +
585 + fprintf(stderr, "uuidmap_acquire_by_uuid(): %.2f ops/sec (%.2f usec/op)\n",
586 + ops, (double)(end_ut - start_ut) / successful);
587 + }
588 +
589 + // Phase 2: Delete everything
590 + fprintf(stderr, "\nDeleting all entries...\n");
591 + start_time = now_monotonic_usec();
592 + next_step = step;
593 +
594 + for(size_t i = 0; i < ENTRIES; i++) {
595 + if (i >= next_step) {
596 + fprintf(stderr, ".");
597 + next_step += step;
598 + }
599 +
600 + UUIDMAP_ID id = entries[i].id;
601 + char uuid_str[UUID_STR_LEN];
602 + uuid_unparse_lower(entries[i].uuid, uuid_str);
603 +
604 + // Test 1: Should exist
605 + nd_uuid_t *uuid_ptr = uuidmap_uuid_ptr(id);
606 + if(!uuid_ptr) {
607 + fprintf(stderr, "\nERROR [%zu]: UUID not found before deletion"
608 + "\n UUID: %s"
609 + "\n ID: %u\n",
610 + i, uuid_str, id);
611 + errors++;
612 + continue;
613 + }
614 +
615 + char current_uuid_str[UUID_STR_LEN];
616 + uuid_unparse_lower(*uuid_ptr, current_uuid_str);
617 + if(uuid_compare(*uuid_ptr, entries[i].uuid) != 0) {
618 + fprintf(stderr, "\nERROR [%zu]: Retrieved UUID doesn't match before deletion"
619 + "\n Original UUID: %s"
620 + "\n Current UUID: %s"
621 + "\n ID: %u\n",
622 + i, uuid_str, current_uuid_str, id);
623 + errors++;
624 + }
625 +
626 + // Test 2: Create again
627 + UUIDMAP_ID id2 = uuidmap_create(entries[i].uuid);
628 + if(id2 != id) {
629 + fprintf(stderr, "\nERROR [%zu]: Recreation returned different ID"
630 + "\n UUID: %s"
631 + "\n Original ID: %u"
632 + "\n New ID: %u\n",
633 + i, uuid_str, id, id2);
634 + errors++;
635 + }
636 +
637 + // Test 3 & 4: Free three times (one extra from benchmark)
638 + uuidmap_free(id);
639 + uuidmap_free(id);
640 + uuidmap_free(id);
641 +
642 + // Test 5: Should be gone
643 + uuid_ptr = uuidmap_uuid_ptr(id);
644 + if(uuid_ptr != NULL) {
645 + char remaining_uuid_str[UUID_STR_LEN];
646 + uuid_unparse_lower(*uuid_ptr, remaining_uuid_str);
647 + fprintf(stderr, "\nERROR [%zu]: UUID still exists after final deletion"
648 + "\n Original UUID: %s"
649 + "\n Remaining UUID: %s"
650 + "\n ID: %u\n",
651 + i, uuid_str, remaining_uuid_str, id);
652 + errors++;
653 + }
654 + }
655 +
656 + end_time = now_monotonic_usec();
657 + fprintf(stderr, "\nPhase 2 completed in %.2f seconds with %d errors\n",
658 + (double)(end_time - start_time) / (double)USEC_PER_SEC, errors);
659 +
660 + freez(entries);
661 +
662 + fprintf(stderr, "\nUUID Map test completed with %d total errors\n", errors);
663 + return errors;
664 +}
src/libnetdata/uuid/uuidmap.h new
+47
@@ -0,0 +1,47 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_UUIDMAP_H
4 +#define NETDATA_UUIDMAP_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +typedef uint32_t UUIDMAP_ID;
9 +
10 +#define UUIDMAP_PARTITIONS 8
11 +
12 +static inline uint8_t uuid_to_uuidmap_partition(const nd_uuid_t uuid) {
13 + return uuid[15] & 0x07; // Mask for 8 partitions (0b111)
14 +}
15 +
16 +static inline uint8_t uuidmap_id_to_partition(UUIDMAP_ID id) {
17 + return (uint8_t)(id >> 29); // Use top 3 bits for partition
18 +}
19 +
20 +static inline UUIDMAP_ID uuidmap_make_id(uint8_t partition, uint32_t id) {
21 + return ((UUIDMAP_ID)partition << 29) | (id & 0x1FFFFFFF); // Use bottom 29 bits for sequence
22 +}
23 +
24 +// returns ID, or zero on error
25 +UUIDMAP_ID uuidmap_create(const nd_uuid_t uuid);
26 +
27 +// delete a uuid from the map
28 +void uuidmap_free(UUIDMAP_ID id);
29 +
30 +// returns true if found, false if not found
31 +// UUID is copied to out_uuid if found
32 +bool uuidmap_uuid(UUIDMAP_ID id, nd_uuid_t out_uuid);
33 +
34 +nd_uuid_t *uuidmap_uuid_ptr(UUIDMAP_ID id);
35 +nd_uuid_t *uuidmap_uuid_ptr_and_dup(UUIDMAP_ID id);
36 +
37 +ND_UUID uuidmap_get(UUIDMAP_ID id);
38 +
39 +size_t uuidmap_memory(void);
40 +size_t uuidmap_free_bytes(void);
41 +struct aral_statistics *uuidmap_aral_statistics(void);
42 +
43 +UUIDMAP_ID uuidmap_dup(UUIDMAP_ID id);
44 +
45 +int uuidmap_unittest(void);
46 +
47 +#endif //NETDATA_UUIDMAP_H
src/ml/ml.cc
+4 -2
@@ -366,7 +366,8 @@ int ml_dimension_load_models(RRDDIM *rd, sqlite3_stmt **active_stmt) {
366 *active_stmt = res;
367 }
368
369 - rc = sqlite3_bind_blob(res, ++param, &dim->rd->metric_uuid, sizeof(dim->rd->metric_uuid), SQLITE_STATIC);
369 + nd_uuid_t *rd_uuid = uuidmap_uuid_ptr(dim->rd->uuid);
370 + rc = sqlite3_bind_blob(res, ++param, rd_uuid, sizeof(*rd_uuid), SQLITE_STATIC);
371 if (unlikely(rc != SQLITE_OK))
372 goto bind_fail;
373
@@ -619,7 +620,8 @@ static void ml_dimension_update_models(ml_worker_t *worker, ml_dimension_t *dim)
620
621 // Add the newly generated model to the list of pending models to flush
622 ml_model_info_t model_info;
622 - uuid_copy(model_info.metric_uuid, dim->rd->metric_uuid);
623 + nd_uuid_t *rd_uuid = uuidmap_uuid_ptr(dim->rd->uuid);
624 + uuid_copy(model_info.metric_uuid, *rd_uuid);
625 model_info.inlined_kmeans = dim->km_contexts.back();
626 worker->pending_model_info.push_back(model_info);
627
src/streaming/stream-sender.c
+4 -5
@@ -606,11 +606,8 @@ bool stream_sender_send_data(struct stream_thread *sth, struct sender_state *s,
606
607 EVLOOP_STATUS status = EVLOOP_STATUS_CONTINUE;
608 while(status == EVLOOP_STATUS_CONTINUE) {
609 - if(!stream_sender_trylock(s)) {
610 - sth->snd.send_misses++;
611 - status = EVLOOP_STATUS_CANT_GET_LOCK;
612 - break;
613 - }
609 + waitq_acquire(&s->waitq, WAITQ_PRIO_URGENT);
610 + stream_sender_lock(s);
611
612 STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(s->scb);
613 char *chunk;
@@ -619,6 +616,7 @@ bool stream_sender_send_data(struct stream_thread *sth, struct sender_state *s,
616 if(!outstanding) {
617 status = EVLOOP_STATUS_NO_MORE_DATA;
618 stream_sender_unlock(s);
619 + waitq_release(&s->waitq);
620 continue;
621 }
622
@@ -652,6 +650,7 @@ bool stream_sender_send_data(struct stream_thread *sth, struct sender_state *s,
650 status = EVLOOP_STATUS_SOCKET_ERROR;
651 }
652 stream_sender_unlock(s);
653 + waitq_release(&s->waitq);
654
655 if (status == EVLOOP_STATUS_SOCKET_ERROR || status == EVLOOP_STATUS_SOCKET_CLOSED) {
656 const char *disconnect_reason = NULL;