@cryptotaxi247 / netdata-1 / commits / 5f72d4279

Streaming improvements No 3 (#19168)

* ML uses synchronous queries * do not call malloc_trim() to free memory, since to locks everything * Reschedule dimensions for training from worker threads. * when we collect or read from the database, it is SAMPLES. When we generate points for a chart is POINTS * keep the receiver send buffer 10x the default * support autoscaling stream circular buffers * nd_poll() prefers sending data vs receiving data - in an attempt to dequeue as soon as possible * fix last commit * allow removing receiver and senders inline, if the stream thread is not working on them * fix logs * Revert "nd_poll() prefers sending data vs receiving data - in an attempt to dequeue as soon as possible" This reverts commit 51539a97dad220bc77b93a48b0110eb033e5528d. * do not access receiver or sender after it has been removed * open cache hot2clean * open cache hot2clean does not need flushing * use aral for extent pages up to 65k * track aral malloc and mmap allocations separately; add 8192 as a possible value to PGD * do not evict too frequently if not needed * fix aral metrics * fix aral metrics again * accurate accounting of memory for dictionaries, strings, labels and MRG * log during shutdown the progress of dbengine flushing * move metasync shutfown after dbengine * max iterations per I/O events * max iterations per I/O events - break the loop * max iterations per I/O events - break the loop - again * disable inline evictions for all caches * when writing to sockets, send everything that can be sent * cleanup code to trigger evictions * fix calculation of eviction size * fix calculation of eviction size once more * fix calculation of eviction size once more - again * ml and replication stop while backfilling is running * process opcodes while draining the sockets; log with limit when asking to disconnect a node * fix log * ml stops when replication queries are running * report pgd_padding to pulse * aral precise memory accounting * removed all alignas() and fix the 2 issues that resulted in unaligned memory accesses (one in mqtt and another in streaming) * remove the bigger sizes from PGD, but keep multiples of gorilla buffers * exclude judy from sanitizers * use 16 bytes alignment on 32 bit machines * internal check about memory alignment * experiment: do not allow more children to connect while there is backfilling or replication queries running * when the node is initializing, retry in 30 seconds * connector cleanup and isolation of control logic about enabling/disabling various parts * stop also health queries while backfilling is running * tuning * drain the input * improve interactivity when suspending * more interactive stream_control * debug logs to find the connection issue * abstracted everything about stream control * Add ml_host_{start,stop} again. * Do not create/update anomaly-detection charts when ML is not running for a host. * rrdhost flag RECEIVER_DISCONNECTED has been reversed to COLLECTOR_ONLINE and has been used for localhost and virtual hosts too, to have a single point of truth about the availability of collected data or not * ml_host_start() and ml_host_stop() are used by streaming receivers; ml_host_start() is used for localhost and virtual hosts * fixed typo * allow up to 3 backfills at a time * add throttling based on user queries * restore cache line paddings * unify streaming logs to make it easier to grep logs * tuning of stream_control * more logs unification * use mallocz_release_as_much_memory_to_the_system() under extreme conditions * do not rely on the response code of evict_pages() * log the gap of the database every time a node is connected * updated ram requirements --------- Co-authored-by: vkalintiris <vasilis@netdata.cloud>

Costa Tsaousis committed Dec 11, 2024 at 18:02 UTC 5f72d4279b9f1ad4e7874c771b62e39e13572315
90 files changed +1792 -1132
CMakeLists.txt
+2
@@ -1549,6 +1549,8 @@ set(STREAMING_PLUGIN_FILES
1549 src/streaming/stream-traffic-types.h
1550 src/streaming/stream-circular-buffer.c
1551 src/streaming/stream-circular-buffer.h
1552 + src/streaming/stream-control.c
1553 + src/streaming/stream-control.h
1554 )
1555
1556 set(WEB_PLUGIN_FILES
docs/netdata-agent/sizing-netdata-agents/ram-requirements.md
+9 -1
@@ -19,7 +19,7 @@ This number can be lowered by limiting the number of Database Tiers or switching
19 | nodes currently received | nodes collected | 512 KiB | Structures and reception buffers |
20 | nodes currently sent | nodes collected | 512 KiB | Structures and dispatch buffers |
21
22 -These numbers vary depending on name length, the number of dimensions per instance and per context, the number and length of the labels added, the number of Machine Learning models maintained and similar parameters. For most use cases, they represent the worst case scenario, so you may find out Netdata actually needs less than that.
22 +These numbers vary depending on metric name length, the average number of dimensions per instance and per context, the number and length of the labels added, the number of database tiers configured, the number of Machine Learning models maintained per metric and similar parameters. For most use cases, they represent the worst case scenario, so you may find out Netdata actually needs less than that.
23
24 Each metric currently being collected needs (1 index + 20 collection + 5 ml) = 26 KiB. When it stops being collected, it needs 1 KiB (index).
25
@@ -84,3 +84,11 @@ We frequently see that the following strategy gives the best results:
84 3. Set the page cache in `netdata.conf` to use 1/3 of the available memory.
85
86 This will allow Netdata queries to have more caches, while leaving plenty of available memory of logs and the operating system.
87 +
88 +In Netdata 2.1 we added the `netdata.conf` option `[db].dbengine use all ram for caches` and `[db].dbengine out of memory protection`.
89 +Combining these two parameters is probably simpler to get best results:
90 +
91 +- `[db].dbengine out of memory protection` is by default 10% of total system RAM, but not more than 5GiB. When the amount of free memory is less than this, Netdata automatically starts releasing memory from its caches to avoid getting out of memory. On `systemd-journal` centralization points, set this to the amount of memory to be dedicated for systemd journal.
92 +- `[db].dbengine use all ram for caches` is by default `no`. Set it to `yes` to use all the memory except the memory given above.
93 +
94 +With these settings, netdata will use all the memory available but leave the amount specified for systemd journal.
src/aclk/mqtt_websockets/mqtt_ng.c
+7 -2
@@ -745,8 +745,13 @@ static size_t mqtt_ng_connect_size(struct mqtt_auth_properties *auth,
745 #define WRITE_POS(frag) (&(frag->data[frag->len]))
746
747 // [MQTT-1.5.2] Two Byte Integer
748 -#define PACK_2B_INT(buffer, integer, frag) { *(uint16_t *)WRITE_POS(frag) = htobe16((integer)); \
749 - DATA_ADVANCE(buffer, sizeof(uint16_t), frag); }
748 +#define PACK_2B_INT(buffer, integer, frag) { \
749 + uint16_t temp = htobe16((integer)); \
750 + memcpy(WRITE_POS(frag), &temp, sizeof(uint16_t)); \
751 + DATA_ADVANCE(buffer, sizeof(uint16_t), frag); \
752 +}
753 +// #define PACK_2B_INT(buffer, integer, frag) { *(uint16_t *)WRITE_POS(frag) = htobe16((integer));
754 +// DATA_ADVANCE(buffer, sizeof(uint16_t), frag); }
755
756 static int _optimized_add(struct header_buffer *buf, void *data, size_t data_len, free_fnc_t data_free_fnc, struct buffer_fragment **frag)
757 {
src/daemon/main.c
+45 -16
@@ -394,11 +394,10 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
394 {
395 watcher_step_complete(WATCHER_STEP_ID_FLUSH_DBENGINE_TIERS);
396 watcher_step_complete(WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS);
397 - watcher_step_complete(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
398 -
397 watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
398 watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
399 watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
400 + watcher_step_complete(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
401 }
402 else
403 {
@@ -406,15 +405,44 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
405
406 #ifdef ENABLE_DBENGINE
407 if(dbengine_enabled) {
408 + nd_log(NDLS_DAEMON, NDLP_INFO, "Preparing DBENGINE shutdown...");
409 for (size_t tier = 0; tier < storage_tiers; tier++)
410 rrdeng_prepare_exit(multidb_ctx[tier]);
411
412 - for (size_t tier = 0; tier < storage_tiers; tier++) {
413 - if (!multidb_ctx[tier])
414 - continue;
415 - completion_wait_for(&multidb_ctx[tier]->quiesce.completion);
416 - completion_destroy(&multidb_ctx[tier]->quiesce.completion);
417 - }
412 + struct pgc_statistics pgc_main_stats = pgc_get_statistics(main_cache);
413 + nd_log(NDLS_DAEMON, NDLP_INFO, "Waiting for DBENGINE to commit unsaved data to disk (%zu pages, %zu bytes)...",
414 + pgc_main_stats.queues[PGC_QUEUE_HOT].entries + pgc_main_stats.queues[PGC_QUEUE_DIRTY].entries,
415 + pgc_main_stats.queues[PGC_QUEUE_HOT].size + pgc_main_stats.queues[PGC_QUEUE_DIRTY].size);
416 +
417 + bool finished_tiers[RRD_STORAGE_TIERS] = { 0 };
418 + size_t waiting_tiers, iterations = 0;
419 + do {
420 + waiting_tiers = 0;
421 + iterations++;
422 +
423 + for (size_t tier = 0; tier < storage_tiers; tier++) {
424 + if (!multidb_ctx[tier] || finished_tiers[tier])
425 + continue;
426 +
427 + waiting_tiers++;
428 + if (completion_timedwait_for(&multidb_ctx[tier]->quiesce.completion, 1)) {
429 + completion_destroy(&multidb_ctx[tier]->quiesce.completion);
430 + finished_tiers[tier] = true;
431 + waiting_tiers--;
432 + nd_log(NDLS_DAEMON, NDLP_INFO, "DBENGINE tier %zu finished!", tier);
433 + }
434 + else if(iterations % 10 == 0) {
435 + pgc_main_stats = pgc_get_statistics(main_cache);
436 + nd_log(NDLS_DAEMON, NDLP_INFO,
437 + "Still waiting for DBENGINE tier %zu to finish "
438 + "(cache still has %zu pages, %zu bytes hot, for all tiers)...",
439 + tier,
440 + pgc_main_stats.queues[PGC_QUEUE_HOT].entries + pgc_main_stats.queues[PGC_QUEUE_DIRTY].entries,
441 + pgc_main_stats.queues[PGC_QUEUE_HOT].size + pgc_main_stats.queues[PGC_QUEUE_DIRTY].size);
442 + }
443 + }
444 + } while(waiting_tiers);
445 + nd_log(NDLS_DAEMON, NDLP_INFO, "DBENGINE shutdown completed...");
446 }
447 #endif
448 watcher_step_complete(WATCHER_STEP_ID_FLUSH_DBENGINE_TIERS);
@@ -422,9 +450,6 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
450 rrd_finalize_collection_for_all_hosts();
451 watcher_step_complete(WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS);
452
425 - metadata_sync_shutdown();
426 - watcher_step_complete(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
427 -
453 #ifdef ENABLE_DBENGINE
454 if(dbengine_enabled) {
455 size_t running = 1;
@@ -452,18 +477,22 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
477 rrdeng_exit(multidb_ctx[tier]);
478 rrdeng_enq_cmd(NULL, RRDENG_OPCODE_SHUTDOWN_EVLOOP, NULL, NULL, STORAGE_PRIORITY_BEST_EFFORT, NULL, NULL);
479 watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
455 - } else {
480 + }
481 + else {
482 // Skip these steps
483 watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
484 watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
485 watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
486 }
487 #else
462 - // Skip these steps
463 - watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
464 - watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
465 - watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
488 + // Skip these steps
489 + watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
490 + watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
491 + watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
492 #endif
493 +
494 + metadata_sync_shutdown();
495 + watcher_step_complete(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
496 }
497
498 // Don't register a shutdown event if we crashed
src/daemon/pulse/pulse-aral.c
+26 -18
@@ -6,7 +6,7 @@
6 struct aral_info {
7 const char *name;
8 RRDSET *st_memory;
9 - RRDDIM *rd_used, *rd_free, *rd_structures;
9 + RRDDIM *rd_malloc_used, *rd_malloc_free, *rd_mmap_used, *rd_mmap_free, *rd_structures, *rd_padding;
10
11 RRDSET *st_utilization;
12 RRDDIM *rd_utilization;
@@ -74,24 +74,26 @@ void pulse_aral_do(bool extended) {
74 if (!stats)
75 continue;
76
77 - size_t allocated_bytes = __atomic_load_n(&stats->malloc.allocated_bytes, __ATOMIC_RELAXED) +
78 - __atomic_load_n(&stats->mmap.allocated_bytes, __ATOMIC_RELAXED);
77 + size_t malloc_allocated_bytes = __atomic_load_n(&stats->malloc.allocated_bytes, __ATOMIC_RELAXED);
78 + size_t malloc_used_bytes = __atomic_load_n(&stats->malloc.used_bytes, __ATOMIC_RELAXED);
79 + if(malloc_used_bytes > malloc_allocated_bytes)
80 + malloc_allocated_bytes = malloc_used_bytes;
81 + size_t malloc_free_bytes = malloc_allocated_bytes - malloc_used_bytes;
82
80 - size_t used_bytes = __atomic_load_n(&stats->malloc.used_bytes, __ATOMIC_RELAXED) +
81 - __atomic_load_n(&stats->mmap.used_bytes, __ATOMIC_RELAXED);
82 -
83 - // slight difference may exist, due to the time needed to get these values
84 - // fix the obvious discrepancies
85 - if(used_bytes > allocated_bytes)
86 - used_bytes = allocated_bytes;
83 + size_t mmap_allocated_bytes = __atomic_load_n(&stats->mmap.allocated_bytes, __ATOMIC_RELAXED);
84 + size_t mmap_used_bytes = __atomic_load_n(&stats->mmap.used_bytes, __ATOMIC_RELAXED);
85 + if(mmap_used_bytes > mmap_allocated_bytes)
86 + mmap_allocated_bytes = mmap_used_bytes;
87 + size_t mmap_free_bytes = mmap_allocated_bytes - mmap_used_bytes;
88
89 size_t structures_bytes = __atomic_load_n(&stats->structures.allocated_bytes, __ATOMIC_RELAXED);
90
90 - size_t free_bytes = allocated_bytes - used_bytes;
91 + size_t padding_bytes = __atomic_load_n(&stats->malloc.padding_bytes, __ATOMIC_RELAXED) +
92 + __atomic_load_n(&stats->mmap.padding_bytes, __ATOMIC_RELAXED);
93
94 NETDATA_DOUBLE utilization;
93 - if(used_bytes && allocated_bytes)
94 - utilization = 100.0 * (NETDATA_DOUBLE)used_bytes / (NETDATA_DOUBLE)allocated_bytes;
95 + if((malloc_used_bytes + mmap_used_bytes != 0) && (malloc_allocated_bytes + mmap_allocated_bytes != 0))
96 + utilization = 100.0 * (NETDATA_DOUBLE)(malloc_used_bytes + mmap_used_bytes) / (NETDATA_DOUBLE)(malloc_allocated_bytes + mmap_allocated_bytes);
97 else
98 utilization = 100.0;
99
@@ -118,14 +120,20 @@ void pulse_aral_do(bool extended) {
120
121 rrdlabels_add(ai->st_memory->rrdlabels, "ARAL", ai->name, RRDLABEL_SRC_AUTO);
122
121 - ai->rd_free = rrddim_add(ai->st_memory, "free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
122 - ai->rd_used = rrddim_add(ai->st_memory, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
123 - ai->rd_structures = rrddim_add(ai->st_memory, "structures", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
123 + ai->rd_malloc_free = rrddim_add(ai->st_memory, "malloc free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
124 + ai->rd_mmap_free = rrddim_add(ai->st_memory, "mmap free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
125 + ai->rd_malloc_used = rrddim_add(ai->st_memory, "malloc used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
126 + ai->rd_mmap_used = rrddim_add(ai->st_memory, "mmap used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
127 + ai->rd_structures = rrddim_add(ai->st_memory, "structures", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
128 + ai->rd_padding = rrddim_add(ai->st_memory, "padding", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
129 }
130
126 - rrddim_set_by_pointer(ai->st_memory, ai->rd_used, (collected_number)allocated_bytes);
127 - rrddim_set_by_pointer(ai->st_memory, ai->rd_free, (collected_number)free_bytes);
131 + rrddim_set_by_pointer(ai->st_memory, ai->rd_malloc_used, (collected_number)malloc_used_bytes);
132 + rrddim_set_by_pointer(ai->st_memory, ai->rd_malloc_free, (collected_number)malloc_free_bytes);
133 + rrddim_set_by_pointer(ai->st_memory, ai->rd_mmap_used, (collected_number)mmap_used_bytes);
134 + rrddim_set_by_pointer(ai->st_memory, ai->rd_mmap_free, (collected_number)mmap_free_bytes);
135 rrddim_set_by_pointer(ai->st_memory, ai->rd_structures, (collected_number)structures_bytes);
136 + rrddim_set_by_pointer(ai->st_memory, ai->rd_padding, (collected_number)padding_bytes);
137 rrdset_done(ai->st_memory);
138 }
139
src/daemon/pulse/pulse-daemon-memory.c
+23 -23
@@ -87,9 +87,7 @@ void pulse_daemon_memory_do(bool extended) {
87 netdata_buffers_statistics.buffers_streaming +
88 netdata_buffers_statistics.cbuffers_streaming +
89 netdata_buffers_statistics.buffers_web +
90 - replication_allocated_buffers() +
91 - aral_by_size_overhead() +
92 - judy_aral_overhead();
90 + replication_allocated_buffers() + aral_by_size_free_bytes() + judy_aral_free_bytes();
91
92 size_t strings = 0;
93 string_statistics(NULL, NULL, NULL, NULL, NULL, &strings, NULL, NULL);
@@ -101,8 +99,7 @@ void pulse_daemon_memory_do(bool extended) {
99 rrddim_set_by_pointer(st_memory, rd_collectors,
100 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_collectors));
101
104 - rrddim_set_by_pointer(st_memory,
105 - rd_rrdhosts,
102 + rrddim_set_by_pointer(st_memory,rd_rrdhosts,
103 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_rrdhost) + (collected_number)netdata_buffers_statistics.rrdhost_allocations_size);
104
105 rrddim_set_by_pointer(st_memory, rd_rrdsets,
@@ -124,14 +121,15 @@ void pulse_daemon_memory_do(bool extended) {
121 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_replication) + (collected_number)replication_allocated_memory());
122 #else
123 uint64_t metadata =
127 - aral_by_size_used_bytes() +
128 - dictionary_stats_category_rrdhost.memory.dict +
129 - dictionary_stats_category_rrdset.memory.dict +
130 - dictionary_stats_category_rrddim.memory.dict +
131 - dictionary_stats_category_rrdcontext.memory.dict +
132 - dictionary_stats_category_rrdhealth.memory.dict +
133 - dictionary_stats_category_functions.memory.dict +
134 - dictionary_stats_category_replication.memory.dict +
124 + aral_by_size_structures_bytes() + aral_by_size_used_bytes() +
125 + dictionary_stats_category_rrdhost.memory.dict + dictionary_stats_category_rrdhost.memory.index +
126 + dictionary_stats_category_rrdset.memory.dict + dictionary_stats_category_rrdset.memory.index +
127 + dictionary_stats_category_rrddim.memory.dict + dictionary_stats_category_rrddim.memory.index +
128 + dictionary_stats_category_rrdcontext.memory.dict + dictionary_stats_category_rrdcontext.memory.index +
129 + dictionary_stats_category_rrdhealth.memory.dict + dictionary_stats_category_rrdhealth.memory.index +
130 + dictionary_stats_category_functions.memory.dict + dictionary_stats_category_functions.memory.index +
131 + dictionary_stats_category_replication.memory.dict + dictionary_stats_category_replication.memory.index +
132 + netdata_buffers_statistics.rrdhost_allocations_size +
133 replication_allocated_memory();
134
135 rrddim_set_by_pointer(st_memory, rd_metadata, (collected_number)metadata);
@@ -157,7 +155,7 @@ void pulse_daemon_memory_do(bool extended) {
155 (collected_number) workers_allocated_memory());
156
157 rrddim_set_by_pointer(st_memory, rd_aral,
160 - (collected_number) aral_by_size_structures());
158 + (collected_number)aral_by_size_structures_bytes());
159
160 rrddim_set_by_pointer(st_memory,
161 rd_judy, (collected_number) judy_aral_structures());
@@ -168,6 +166,13 @@ void pulse_daemon_memory_do(bool extended) {
166 rrdset_done(st_memory);
167 }
168
169 + // ----------------------------------------------------------------------------------------------------------------
170 +
171 + if(!extended)
172 + return;
173 +
174 + // ----------------------------------------------------------------------------------------------------------------
175 +
176 {
177 static RRDSET *st_memory_buffers = NULL;
178 static RRDDIM *rd_queries = NULL;
@@ -212,8 +217,8 @@ void pulse_daemon_memory_do(bool extended) {
217 rd_cbuffers_streaming = rrddim_add(st_memory_buffers, "streaming cbuf", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
218 rd_buffers_replication = rrddim_add(st_memory_buffers, "replication", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
219 rd_buffers_web = rrddim_add(st_memory_buffers, "web", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
215 - rd_buffers_aral = rrddim_add(st_memory_buffers, "aral", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
216 - rd_buffers_judy = rrddim_add(st_memory_buffers, "judy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
220 + rd_buffers_aral = rrddim_add(st_memory_buffers, "aral-by-size free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
221 + rd_buffers_judy = rrddim_add(st_memory_buffers, "aral-judy free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
222 }
223
224 rrddim_set_by_pointer(st_memory_buffers, rd_queries, (collected_number)netdata_buffers_statistics.query_targets_size + (collected_number) onewayalloc_allocated_memory());
@@ -228,17 +233,12 @@ void pulse_daemon_memory_do(bool extended) {
233 rrddim_set_by_pointer(st_memory_buffers, rd_cbuffers_streaming, (collected_number)netdata_buffers_statistics.cbuffers_streaming);
234 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_replication, (collected_number)replication_allocated_buffers());
235 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_web, (collected_number)netdata_buffers_statistics.buffers_web);
231 - rrddim_set_by_pointer(st_memory_buffers, rd_buffers_aral, (collected_number)aral_by_size_overhead());
232 - rrddim_set_by_pointer(st_memory_buffers, rd_buffers_judy, (collected_number)judy_aral_overhead());
236 + rrddim_set_by_pointer(st_memory_buffers, rd_buffers_aral, (collected_number)aral_by_size_free_bytes());
237 + rrddim_set_by_pointer(st_memory_buffers, rd_buffers_judy, (collected_number)judy_aral_free_bytes());
238
239 rrdset_done(st_memory_buffers);
240 }
241
242 // ----------------------------------------------------------------------------------------------------------------
243
239 - if(!extended)
240 - return;
241 -
242 - // ----------------------------------------------------------------------------------------------------------------
243 -
244 }
src/daemon/pulse/pulse-dbengine.c
+41 -21
@@ -668,15 +668,26 @@ void pulse_dbengine_do(bool extended) {
668 mrg_stats_old = mrg_stats;
669 mrg_get_statistics(main_mrg, &mrg_stats);
670
671 - struct rrdeng_buffer_sizes buffers = rrdeng_get_buffer_sizes();
672 - size_t buffers_total_size = buffers.handles + buffers.xt_buf + buffers.xt_io + buffers.pdc + buffers.descriptors +
673 - buffers.opcodes + buffers.wal + buffers.workers + buffers.epdl + buffers.deol + buffers.pd + buffers.pgc + buffers.pgd + buffers.mrg;
671 + struct rrdeng_buffer_sizes dbmem = rrdeng_pulse_memory_sizes();
672
673 + size_t buffers_total_size = dbmem.xt_buf + dbmem.wal;
674 #ifdef PDC_USE_JULYL
675 buffers_total_size += buffers.julyl;
676 #endif
677
679 - pulse_dbengine_total_memory = pgc_main_stats.size + pgc_open_stats.size + pgc_extent_stats.size + mrg_stats.size + buffers_total_size;
678 + size_t aral_structures_total_size = 0, aral_used_total_size = 0;
679 + size_t aral_padding_total_size = 0;
680 + for(size_t i = 0; i < RRDENG_MEM_MAX ; i++) {
681 + buffers_total_size += aral_free_bytes_from_stats(dbmem.as[i]);
682 + aral_structures_total_size += aral_structures_bytes_from_stats(dbmem.as[i]);
683 + aral_used_total_size += aral_used_bytes_from_stats(dbmem.as[i]);
684 + aral_padding_total_size += aral_padding_bytes_from_stats(dbmem.as[i]);
685 + }
686 +
687 + pulse_dbengine_total_memory =
688 + pgc_main_stats.size + (ssize_t)pgc_open_stats.size + pgc_extent_stats.size +
689 + mrg_stats.size +
690 + buffers_total_size + aral_structures_total_size + aral_padding_total_size + pgd_padding_bytes();
691
692 size_t priority = 135000;
693
@@ -687,6 +698,9 @@ void pulse_dbengine_do(bool extended) {
698 static RRDDIM *rd_pgc_memory_extent = NULL; // extent compresses cache memory
699 static RRDDIM *rd_pgc_memory_metrics = NULL; // metric registry memory
700 static RRDDIM *rd_pgc_memory_buffers = NULL;
701 + static RRDDIM *rd_pgc_memory_aral_padding = NULL;
702 + static RRDDIM *rd_pgc_memory_pgd_padding = NULL;
703 + static RRDDIM *rd_pgc_memory_aral_structures = NULL;
704
705 if (unlikely(!st_pgc_memory)) {
706 st_pgc_memory = rrdset_create_localhost(
@@ -708,6 +722,9 @@ void pulse_dbengine_do(bool extended) {
722 rd_pgc_memory_extent = rrddim_add(st_pgc_memory, "extent cache", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
723 rd_pgc_memory_metrics = rrddim_add(st_pgc_memory, "metrics registry", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
724 rd_pgc_memory_buffers = rrddim_add(st_pgc_memory, "buffers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
725 + rd_pgc_memory_aral_padding = rrddim_add(st_pgc_memory, "aral padding", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
726 + rd_pgc_memory_pgd_padding = rrddim_add(st_pgc_memory, "pgd padding", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
727 + rd_pgc_memory_aral_structures = rrddim_add(st_pgc_memory, "aral structures", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
728 }
729 priority++;
730
@@ -717,6 +734,9 @@ void pulse_dbengine_do(bool extended) {
734 rrddim_set_by_pointer(st_pgc_memory, rd_pgc_memory_extent, (collected_number)pgc_extent_stats.size);
735 rrddim_set_by_pointer(st_pgc_memory, rd_pgc_memory_metrics, (collected_number)mrg_stats.size);
736 rrddim_set_by_pointer(st_pgc_memory, rd_pgc_memory_buffers, (collected_number)buffers_total_size);
737 + rrddim_set_by_pointer(st_pgc_memory, rd_pgc_memory_aral_padding, (collected_number)aral_padding_total_size);
738 + rrddim_set_by_pointer(st_pgc_memory, rd_pgc_memory_pgd_padding, (collected_number)pgd_padding_bytes());
739 + rrddim_set_by_pointer(st_pgc_memory, rd_pgc_memory_aral_structures, (collected_number)aral_structures_total_size);
740
741 rrdset_done(st_pgc_memory);
742 }
@@ -756,9 +776,9 @@ void pulse_dbengine_do(bool extended) {
776 localhost->rrd_update_every,
777 RRDSET_TYPE_STACKED);
778
759 - rd_pgc_buffers_pgc = rrddim_add(st_pgc_buffers, "pgc", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
760 - rd_pgc_buffers_pgd = rrddim_add(st_pgc_buffers, "pgd", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
761 - rd_pgc_buffers_mrg = rrddim_add(st_pgc_buffers, "mrg", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
779 + rd_pgc_buffers_pgc = rrddim_add(st_pgc_buffers, "pgc", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
780 + rd_pgc_buffers_pgd = rrddim_add(st_pgc_buffers, "pgd", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
781 + rd_pgc_buffers_mrg = rrddim_add(st_pgc_buffers, "mrg", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
782 rd_pgc_buffers_opcodes = rrddim_add(st_pgc_buffers, "opcodes", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
783 rd_pgc_buffers_handles = rrddim_add(st_pgc_buffers, "query handles", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
784 rd_pgc_buffers_descriptors = rrddim_add(st_pgc_buffers, "descriptors", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
@@ -776,20 +796,20 @@ void pulse_dbengine_do(bool extended) {
796 }
797 priority++;
798
779 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pgc, (collected_number)buffers.pgc);
780 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pgd, (collected_number)buffers.pgd);
781 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_mrg, (collected_number)buffers.mrg);
782 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_opcodes, (collected_number)buffers.opcodes);
783 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_handles, (collected_number)buffers.handles);
784 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_descriptors, (collected_number)buffers.descriptors);
785 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_wal, (collected_number)buffers.wal);
786 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_workers, (collected_number)buffers.workers);
787 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pdc, (collected_number)buffers.pdc);
788 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pd, (collected_number)buffers.pd);
789 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_xt_io, (collected_number)buffers.xt_io);
790 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_xt_buf, (collected_number)buffers.xt_buf);
791 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_epdl, (collected_number)buffers.epdl);
792 - rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_deol, (collected_number)buffers.deol);
799 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pgc, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_PGC]));
800 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pgd, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_PGD]));
801 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_mrg, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_MRG]));
802 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_opcodes, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_OPCODES]));
803 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_handles, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_HANDLES]));
804 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_descriptors, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_DESCRIPTORS]));
805 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_wal, (collected_number)dbmem.wal);
806 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_workers, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_WORKERS]));
807 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pdc, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_PDC]));
808 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_pd, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_PD]));
809 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_xt_io, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_XT_IO]));
810 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_xt_buf, (collected_number)dbmem.xt_buf);
811 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_epdl, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_EPDL]));
812 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_deol, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_DEOL]));
813 #ifdef PDC_USE_JULYL
814 rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_julyl, (collected_number)buffers.julyl);
815 #endif
src/daemon/pulse/pulse-gorilla.c
+9 -5
@@ -6,11 +6,15 @@
6 static struct gorilla_statistics {
7 bool enabled;
8
9 - alignas(64) uint64_t tier0_hot_gorilla_buffers;
10 -
11 - alignas(64) uint64_t gorilla_tier0_disk_actual_bytes;
12 - alignas(64) uint64_t gorilla_tier0_disk_optimal_bytes;
13 - alignas(64) uint64_t gorilla_tier0_disk_original_bytes;
9 + CACHE_LINE_PADDING();
10 + uint64_t tier0_hot_gorilla_buffers;
11 +
12 + CACHE_LINE_PADDING();
13 + uint64_t gorilla_tier0_disk_actual_bytes;
14 + CACHE_LINE_PADDING();
15 + uint64_t gorilla_tier0_disk_optimal_bytes;
16 + CACHE_LINE_PADDING();
17 + uint64_t gorilla_tier0_disk_original_bytes;
18 } gorilla_statistics = { 0 };
19
20 void pulse_gorilla_hot_buffer_added() {
src/daemon/pulse/pulse-http-api.c
+9
@@ -8,16 +8,25 @@
8 static struct web_statistics {
9 bool extended;
10
11 + CACHE_LINE_PADDING();
12 uint16_t connected_clients;
13 + CACHE_LINE_PADDING();
14 uint64_t web_client_count; // oops! this is used for giving unique IDs to web_clients!
15
16 + CACHE_LINE_PADDING();
17 uint64_t web_requests;
18 + CACHE_LINE_PADDING();
19 uint64_t web_usec;
20 + CACHE_LINE_PADDING();
21 uint64_t web_usec_max;
22 + CACHE_LINE_PADDING();
23 uint64_t bytes_received;
24 + CACHE_LINE_PADDING();
25 uint64_t bytes_sent;
26
27 + CACHE_LINE_PADDING();
28 uint64_t content_size_uncompressed;
29 + CACHE_LINE_PADDING();
30 uint64_t content_size_compressed;
31 } web_statistics;
32
src/daemon/pulse/pulse-ml.c
+16 -8
@@ -4,14 +4,22 @@
4 #include "pulse-ml.h"
5
6 static struct ml_statistics {
7 - alignas(64) uint64_t ml_models_consulted;
8 - alignas(64) uint64_t ml_models_received;
9 - alignas(64) uint64_t ml_models_ignored;
10 - alignas(64) uint64_t ml_models_sent;
11 - alignas(64) uint64_t ml_models_deserialization_failures;
12 - alignas(64) uint64_t ml_memory_consumption;
13 - alignas(64) uint64_t ml_memory_new;
14 - alignas(64) uint64_t ml_memory_delete;
7 + CACHE_LINE_PADDING();
8 + uint64_t ml_models_consulted;
9 + CACHE_LINE_PADDING();
10 + uint64_t ml_models_received;
11 + CACHE_LINE_PADDING();
12 + uint64_t ml_models_ignored;
13 + CACHE_LINE_PADDING();
14 + uint64_t ml_models_sent;
15 + CACHE_LINE_PADDING();
16 + uint64_t ml_models_deserialization_failures;
17 + CACHE_LINE_PADDING();
18 + uint64_t ml_memory_consumption;
19 + CACHE_LINE_PADDING();
20 + uint64_t ml_memory_new;
21 + CACHE_LINE_PADDING();
22 + uint64_t ml_memory_delete;
23 } ml_statistics = {0};
24
25 void pulse_ml_models_received()
src/daemon/pulse/pulse-queries.c
+22 -3
@@ -5,30 +5,49 @@
5 #include "streaming/replication.h"
6
7 static struct query_statistics {
8 + CACHE_LINE_PADDING();
9 uint64_t api_data_queries_made;
10 + CACHE_LINE_PADDING();
11 uint64_t api_data_db_points_read;
12 + CACHE_LINE_PADDING();
13 uint64_t api_data_result_points_generated;
14
15 + CACHE_LINE_PADDING();
16 uint64_t api_weights_queries_made;
17 + CACHE_LINE_PADDING();
18 uint64_t api_weights_db_points_read;
19 + CACHE_LINE_PADDING();
20 uint64_t api_weights_result_points_generated;
21
22 + CACHE_LINE_PADDING();
23 uint64_t api_badges_queries_made;
24 + CACHE_LINE_PADDING();
25 uint64_t api_badges_db_points_read;
26 + CACHE_LINE_PADDING();
27 uint64_t api_badges_result_points_generated;
28
29 + CACHE_LINE_PADDING();
30 uint64_t health_queries_made;
31 + CACHE_LINE_PADDING();
32 uint64_t health_db_points_read;
33 + CACHE_LINE_PADDING();
34 uint64_t health_result_points_generated;
35
36 + CACHE_LINE_PADDING();
37 uint64_t ml_queries_made;
38 + CACHE_LINE_PADDING();
39 uint64_t ml_db_points_read;
40 + CACHE_LINE_PADDING();
41 uint64_t ml_result_points_generated;
42
43 + CACHE_LINE_PADDING();
44 uint64_t backfill_queries_made;
45 + CACHE_LINE_PADDING();
46 uint64_t backfill_db_points_read;
47
48 + CACHE_LINE_PADDING();
49 uint64_t exporters_queries_made;
50 + CACHE_LINE_PADDING();
51 uint64_t exporters_db_points_read;
52 } query_statistics;
53
@@ -182,12 +201,12 @@ void pulse_queries_do(bool extended __maybe_unused) {
201 if (unlikely(!st_points_read)) {
202 st_points_read = rrdset_create_localhost(
203 "netdata"
185 - , "db_points_read"
204 + , "db_samples_read"
205 , NULL
206 , "Time-Series Queries"
207 , NULL
208 , "Netdata Time-Series DB Samples Read"
190 - , "points/s"
209 + , "samples/s"
210 , "netdata"
211 , "pulse"
212 , 131001
@@ -233,7 +252,7 @@ void pulse_queries_do(bool extended __maybe_unused) {
252 , NULL
253 , "Time-Series Queries"
254 , NULL
236 - , "Netdata Time-Series Samples Generated"
255 + , "Netdata Time-Series Points Generated"
256 , "points/s"
257 , "netdata"
258 , "pulse"
src/daemon/pulse/pulse-sqlite3.c
+28 -14
@@ -6,20 +6,34 @@
6 static struct sqlite3_statistics {
7 bool enabled;
8
9 - alignas(64) uint64_t sqlite3_queries_made;
10 - alignas(64) uint64_t sqlite3_queries_ok;
11 - alignas(64) uint64_t sqlite3_queries_failed;
12 - alignas(64) uint64_t sqlite3_queries_failed_busy;
13 - alignas(64) uint64_t sqlite3_queries_failed_locked;
14 - alignas(64) uint64_t sqlite3_rows;
15 - alignas(64) uint64_t sqlite3_metadata_cache_hit;
16 - alignas(64) uint64_t sqlite3_context_cache_hit;
17 - alignas(64) uint64_t sqlite3_metadata_cache_miss;
18 - alignas(64) uint64_t sqlite3_context_cache_miss;
19 - alignas(64) uint64_t sqlite3_metadata_cache_spill;
20 - alignas(64) uint64_t sqlite3_context_cache_spill;
21 - alignas(64) uint64_t sqlite3_metadata_cache_write;
22 - alignas(64) uint64_t sqlite3_context_cache_write;
9 + CACHE_LINE_PADDING();
10 + uint64_t sqlite3_queries_made;
11 + CACHE_LINE_PADDING();
12 + uint64_t sqlite3_queries_ok;
13 + CACHE_LINE_PADDING();
14 + uint64_t sqlite3_queries_failed;
15 + CACHE_LINE_PADDING();
16 + uint64_t sqlite3_queries_failed_busy;
17 + CACHE_LINE_PADDING();
18 + uint64_t sqlite3_queries_failed_locked;
19 + CACHE_LINE_PADDING();
20 + uint64_t sqlite3_rows;
21 + CACHE_LINE_PADDING();
22 + uint64_t sqlite3_metadata_cache_hit;
23 + CACHE_LINE_PADDING();
24 + uint64_t sqlite3_context_cache_hit;
25 + CACHE_LINE_PADDING();
26 + uint64_t sqlite3_metadata_cache_miss;
27 + CACHE_LINE_PADDING();
28 + uint64_t sqlite3_context_cache_miss;
29 + CACHE_LINE_PADDING();
30 + uint64_t sqlite3_metadata_cache_spill;
31 + CACHE_LINE_PADDING();
32 + uint64_t sqlite3_context_cache_spill;
33 + CACHE_LINE_PADDING();
34 + uint64_t sqlite3_metadata_cache_write;
35 + CACHE_LINE_PADDING();
36 + uint64_t sqlite3_context_cache_write;
37 } sqlite3_statistics = { };
38
39 void pulse_sqlite3_query_completed(bool success, bool busy, bool locked) {
src/daemon/watcher.c
+3 -3
@@ -82,10 +82,10 @@ void *watcher_main(void *arg)
82 watcher_wait_for_step(WATCHER_STEP_ID_CANCEL_MAIN_THREADS);
83 watcher_wait_for_step(WATCHER_STEP_ID_FLUSH_DBENGINE_TIERS);
84 watcher_wait_for_step(WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS);
85 - watcher_wait_for_step(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
85 watcher_wait_for_step(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
86 watcher_wait_for_step(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
87 watcher_wait_for_step(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
88 + watcher_wait_for_step(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
89 watcher_wait_for_step(WATCHER_STEP_ID_CLOSE_SQL_DATABASES);
90 watcher_wait_for_step(WATCHER_STEP_ID_REMOVE_PID_FILE);
91 watcher_wait_for_step(WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES);
@@ -140,14 +140,14 @@ void watcher_thread_start() {
140 "flush dbengine tiers";
141 watcher_steps[WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS].msg =
142 "stop collection for all hosts";
143 - watcher_steps[WATCHER_STEP_ID_STOP_METASYNC_THREADS].msg =
144 - "stop metasync threads";
143 watcher_steps[WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH].msg =
144 "wait for dbengine collectors to finish";
145 watcher_steps[WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING].msg =
146 "wait for dbengine main cache to finish flushing";
147 watcher_steps[WATCHER_STEP_ID_STOP_DBENGINE_TIERS].msg =
148 "stop dbengine tiers";
149 + watcher_steps[WATCHER_STEP_ID_STOP_METASYNC_THREADS].msg =
150 + "stop metasync threads";
151 watcher_steps[WATCHER_STEP_ID_CLOSE_SQL_DATABASES].msg =
152 "close SQL databases";
153 watcher_steps[WATCHER_STEP_ID_REMOVE_PID_FILE].msg =
src/daemon/watcher.h
+1 -1
@@ -24,10 +24,10 @@ typedef enum {
24 WATCHER_STEP_ID_CANCEL_MAIN_THREADS,
25 WATCHER_STEP_ID_FLUSH_DBENGINE_TIERS,
26 WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS,
27 - WATCHER_STEP_ID_STOP_METASYNC_THREADS,
27 WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH,
28 WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING,
29 WATCHER_STEP_ID_STOP_DBENGINE_TIERS,
30 + WATCHER_STEP_ID_STOP_METASYNC_THREADS,
31 WATCHER_STEP_ID_CLOSE_SQL_DATABASES,
32 WATCHER_STEP_ID_REMOVE_PID_FILE,
33 WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES,
src/database/engine/cache.c
+91 -66
@@ -71,7 +71,7 @@ struct pgc_page {
71 };
72
73 struct pgc_queue {
74 - alignas(64) SPINLOCK spinlock;
74 + SPINLOCK spinlock;
75 union {
76 PGC_PAGE *base;
77 Pvoid_t sections_judy;
@@ -113,13 +113,12 @@ struct pgc {
113 } config;
114
115 struct {
116 - SPINLOCK spinlock; // when locked, the evict_thread is currently evicting pages
116 ND_THREAD *thread; // the thread
117 struct completion completion; // signal the thread to wake up
118 } evictor;
119
120 struct pgc_index {
122 - alignas(64) RW_SPINLOCK rw_spinlock;
121 + RW_SPINLOCK rw_spinlock;
122 Pvoid_t sections_judy;
123 #ifdef PGC_WITH_ARAL
124 ARAL *aral;
@@ -127,7 +126,7 @@ struct pgc {
126 } *index;
127
128 struct {
130 - alignas(64) SPINLOCK spinlock;
129 + SPINLOCK spinlock;
130 size_t per1000;
131 } usage;
132
@@ -137,7 +136,7 @@ struct pgc {
136 struct pgc_statistics stats; // statistics
137
138 #ifdef NETDATA_PGC_POINTER_CHECK
140 - alignas(64) netdata_mutex_t global_pointer_registry_mutex;
139 + netdata_mutex_t global_pointer_registry_mutex;
140 Pvoid_t global_pointer_registry;
141 #endif
142 };
@@ -343,6 +342,20 @@ static inline void pgc_size_histogram_del(PGC *cache, struct pgc_size_histogram
342 // ----------------------------------------------------------------------------
343 // evictions control
344
345 +static inline uint64_t pgc_threshold(size_t threshold, uint64_t wanted, uint64_t current, uint64_t clean) {
346 + if(current < clean)
347 + current = clean;
348 +
349 + if(wanted < current - clean)
350 + wanted = current - clean;
351 +
352 + uint64_t ret = wanted * threshold / 1000ULL;
353 + if(ret < current - clean)
354 + ret = current - clean;
355 +
356 + return ret;
357 +}
358 +
359 static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
360
361 if(size_to_evict)
@@ -351,33 +364,33 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
364 else if(!spinlock_trylock(&cache->usage.spinlock))
365 return __atomic_load_n(&cache->usage.per1000, __ATOMIC_RELAXED);
366
354 - size_t wanted_cache_size;
367 + uint64_t wanted_cache_size;
368
356 - const size_t dirty = __atomic_load_n(&cache->dirty.stats->size, __ATOMIC_RELAXED);
357 - const size_t hot = __atomic_load_n(&cache->hot.stats->size, __ATOMIC_RELAXED);
358 - const size_t clean = __atomic_load_n(&cache->clean.stats->size, __ATOMIC_RELAXED);
359 - const size_t evicting = __atomic_load_n(&cache->stats.evicting_size, __ATOMIC_RELAXED);
360 - const size_t flushing = __atomic_load_n(&cache->stats.flushing_size, __ATOMIC_RELAXED);
361 - const size_t current_cache_size = __atomic_load_n(&cache->stats.size, __ATOMIC_RELAXED);
362 - const size_t all_pages_size = hot + dirty + clean + evicting + flushing;
363 - const size_t index = current_cache_size > all_pages_size ? current_cache_size - all_pages_size : 0;
364 - const size_t referenced_size = __atomic_load_n(&cache->stats.referenced_size, __ATOMIC_RELAXED);
369 + const uint64_t dirty = __atomic_load_n(&cache->dirty.stats->size, __ATOMIC_RELAXED);
370 + const uint64_t hot = __atomic_load_n(&cache->hot.stats->size, __ATOMIC_RELAXED);
371 + const uint64_t clean = __atomic_load_n(&cache->clean.stats->size, __ATOMIC_RELAXED);
372 + const uint64_t evicting = __atomic_load_n(&cache->stats.evicting_size, __ATOMIC_RELAXED);
373 + const uint64_t flushing = __atomic_load_n(&cache->stats.flushing_size, __ATOMIC_RELAXED);
374 + const uint64_t current_cache_size = __atomic_load_n(&cache->stats.size, __ATOMIC_RELAXED);
375 + const uint64_t all_pages_size = hot + dirty + clean + evicting + flushing;
376 + const uint64_t index = current_cache_size > all_pages_size ? current_cache_size - all_pages_size : 0;
377 + const uint64_t referenced_size = __atomic_load_n(&cache->stats.referenced_size, __ATOMIC_RELAXED);
378
379 if(cache->config.options & PGC_OPTIONS_AUTOSCALE) {
367 - const size_t dirty_max = __atomic_load_n(&cache->dirty.stats->max_size, __ATOMIC_RELAXED);
368 - const size_t hot_max = __atomic_load_n(&cache->hot.stats->max_size, __ATOMIC_RELAXED);
380 + const uint64_t dirty_max = __atomic_load_n(&cache->dirty.stats->max_size, __ATOMIC_RELAXED);
381 + const uint64_t hot_max = __atomic_load_n(&cache->hot.stats->max_size, __ATOMIC_RELAXED);
382
383 // our promise to users
371 - const size_t max_size1 = MAX(hot_max, hot) * 2;
384 + const uint64_t max_size1 = MAX(hot_max, hot) * 2;
385
386 // protection against slow flushing
374 - const size_t max_size2 = hot_max + ((dirty_max * 2 < hot_max * 2 / 3) ? hot_max * 2 / 3 : dirty_max * 2) + index;
387 + const uint64_t max_size2 = hot_max + ((dirty_max * 2 < hot_max * 2 / 3) ? hot_max * 2 / 3 : dirty_max * 2) + index;
388
389 // the final wanted cache size
390 wanted_cache_size = MIN(max_size1, max_size2);
391
392 if(cache->config.dynamic_target_size_cb) {
380 - const size_t wanted_cache_size_cb = cache->config.dynamic_target_size_cb();
393 + const uint64_t wanted_cache_size_cb = cache->config.dynamic_target_size_cb();
394 if(wanted_cache_size_cb > wanted_cache_size)
395 wanted_cache_size = wanted_cache_size_cb;
396 }
@@ -395,21 +408,19 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
408 wanted_cache_size = referenced_size + dirty;
409
410 // if we don't have enough clean pages, there is no reason to be aggressive or critical
398 - if(current_cache_size > wanted_cache_size && wanted_cache_size < current_cache_size - clean)
411 + if(wanted_cache_size < current_cache_size - clean)
412 wanted_cache_size = current_cache_size - clean;
413
401 - bool signal_the_evictor = false;
414 if(cache->config.out_of_memory_protection_bytes) {
415 // out of memory protection
416 OS_SYSTEM_MEMORY sm = os_system_memory(false);
417 if(sm.ram_total_bytes) {
418 // when the total exists, ram_available_bytes is also right
419
408 - const size_t min_available = cache->config.out_of_memory_protection_bytes;
420 + const uint64_t min_available = cache->config.out_of_memory_protection_bytes;
421 if (sm.ram_available_bytes < min_available) {
422 // we must shrink
423 wanted_cache_size = current_cache_size - (min_available - sm.ram_available_bytes);
412 - signal_the_evictor = true;
424 }
425 else if(cache->config.use_all_ram) {
426 // we can grow
@@ -418,38 +429,40 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
429 }
430 }
431
421 - const size_t per1000 = (size_t)((unsigned long long)current_cache_size * 1000ULL / (unsigned long long)wanted_cache_size);
422 -
432 + const size_t per1000 = (size_t)(current_cache_size * 1000ULL / wanted_cache_size);
433 __atomic_store_n(&cache->usage.per1000, per1000, __ATOMIC_RELAXED);
434 __atomic_store_n(&cache->stats.wanted_cache_size, wanted_cache_size, __ATOMIC_RELAXED);
435 __atomic_store_n(&cache->stats.current_cache_size, current_cache_size, __ATOMIC_RELAXED);
436
427 - spinlock_unlock(&cache->usage.spinlock);
428 -
429 - if(size_to_evict) {
430 - size_t target = (size_t)((uint64_t)wanted_cache_size * (uint64_t)cache->config.evict_low_threshold_per1000 / 1000ULL);
431 -
432 - if(target < wanted_cache_size - clean)
433 - target = wanted_cache_size - clean;
437 + uint64_t healthy_target = pgc_threshold(cache->config.healthy_size_per1000, wanted_cache_size, current_cache_size, clean);
438 + if(current_cache_size > healthy_target) {
439 + uint64_t low_watermark_target = pgc_threshold(cache->config.evict_low_threshold_per1000, wanted_cache_size, current_cache_size, clean);
440
435 - if(current_cache_size > target)
436 - *size_to_evict = current_cache_size - target;
437 - else
438 - *size_to_evict = 0;
439 - }
441 + uint64_t size_to_evict_now = current_cache_size - low_watermark_target;
442 + if(size_to_evict_now > clean)
443 + size_to_evict_now = clean;
444
441 - if(per1000 >= cache->config.severe_pressure_per1000)
442 - __atomic_add_fetch(&cache->stats.events_cache_under_severe_pressure, 1, __ATOMIC_RELAXED);
445 + if(size_to_evict)
446 + *size_to_evict = (size_t)size_to_evict_now;
447
444 - else if(per1000 >= cache->config.aggressive_evict_per1000)
445 - __atomic_add_fetch(&cache->stats.events_cache_needs_space_aggressively, 1, __ATOMIC_RELAXED);
448 + bool signal = false;
449 + if(per1000 >= cache->config.severe_pressure_per1000) {
450 + __atomic_add_fetch(&cache->stats.events_cache_under_severe_pressure, 1, __ATOMIC_RELAXED);
451 + signal = true;
452 + }
453 + else if(per1000 >= cache->config.aggressive_evict_per1000) {
454 + __atomic_add_fetch(&cache->stats.events_cache_needs_space_aggressively, 1, __ATOMIC_RELAXED);
455 + signal = true;
456 + }
457
447 - if (signal_the_evictor && spinlock_trylock(&cache->evictor.spinlock)) {
448 - completion_mark_complete_a_job(&cache->evictor.completion);
449 - spinlock_unlock(&cache->evictor.spinlock);
450 - __atomic_add_fetch(&cache->stats.waste_evict_thread_signals, 1, __ATOMIC_RELAXED);
458 + if(signal) {
459 + completion_mark_complete_a_job(&cache->evictor.completion);
460 + __atomic_add_fetch(&cache->stats.waste_evict_thread_signals, 1, __ATOMIC_RELAXED);
461 + }
462 }
463
464 + spinlock_unlock(&cache->usage.spinlock);
465 +
466 return per1000;
467 }
468
@@ -558,7 +571,7 @@ struct section_pages {
571 PGC_PAGE *base;
572 };
573
561 -static struct aral_statistics aral_statistics_for_pgc = { 0 };
574 +static struct aral_statistics pgc_aral_statistics = { 0 };
575
576 static ARAL *pgc_sections_aral = NULL;
577
@@ -1169,6 +1182,7 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1182 else if(unlikely(wait)) {
1183 // evict as many as necessary for the cache to go at the predefined threshold
1184 per1000 = cache_usage_per1000(cache, &max_size_to_evict);
1185 + max_size_to_evict /= 2; // do it in 2 steps
1186 if(per1000 >= cache->config.severe_pressure_per1000) {
1187 under_sever_pressure = true;
1188 max_pages_to_evict = max_pages_to_evict ? max_pages_to_evict * 2 : 4096;
@@ -1934,30 +1948,35 @@ static void *pgc_evict_thread(void *ptr) {
1948 worker_register_job_name(0, "signaled");
1949 worker_register_job_name(1, "scheduled");
1950
1937 - unsigned job_id = 0;
1951 + unsigned job_id = 0, severe_pressure_counter = 0;
1952
1953 while (true) {
1954 worker_is_idle();
1955 unsigned new_job_id = completion_wait_for_a_job_with_timeout(
1942 - &cache->evictor.completion, job_id, 100);
1956 + &cache->evictor.completion, job_id, 1000);
1957
1944 - bool was_signaled = new_job_id > job_id;
1945 - worker_is_busy(was_signaled ? 1 : 0);
1958 + worker_is_busy(new_job_id > job_id ? 1 : 0);
1959 job_id = new_job_id;
1960
1961 if (nd_thread_signaled_to_cancel())
1962 return NULL;
1963
1964 + evict_pages(cache, 0, 0, true, false);
1965 +
1966 size_t size_to_evict = 0;
1952 - size_t per1000 = cache_usage_per1000(cache, &size_to_evict);
1953 - bool was_critical = per1000 >= cache->config.severe_pressure_per1000;
1967 + if(cache_usage_per1000(cache, &size_to_evict) > cache->config.severe_pressure_per1000) {
1968 + severe_pressure_counter++;
1969
1955 - if(size_to_evict > 0) {
1956 - evict_pages(cache, 0, 0, true, false);
1970 + if(severe_pressure_counter > 100) {
1971 + // so, we tried 100 times to reduce memory,
1972 + // but it is still severe!
1973
1958 - if (was_signaled || was_critical)
1974 mallocz_release_as_much_memory_to_the_system();
1975 + severe_pressure_counter = 0;
1976 + }
1977 }
1978 + else
1979 + severe_pressure_counter = 0;
1980 }
1981
1982 worker_unregister();
@@ -2040,7 +2059,7 @@ PGC *pgc_create(const char *name,
2059 sizeof(PGC_PAGE) + cache->config.additional_bytes_per_page,
2060 0,
2061 0,
2043 - &aral_statistics_for_pgc,
2062 + &pgc_aral_statistics,
2063 NULL,
2064 NULL,
2065 false,
@@ -2075,7 +2094,6 @@ PGC *pgc_create(const char *name,
2094
2095 // last create the eviction thread
2096 {
2078 - spinlock_init(&cache->evictor.spinlock);
2097 completion_init(&cache->evictor.completion);
2098 cache->evictor.thread = nd_thread_create(name, NETDATA_THREAD_OPTION_JOINABLE, pgc_evict_thread, cache);
2099 }
@@ -2083,12 +2101,8 @@ PGC *pgc_create(const char *name,
2101 return cache;
2102 }
2103
2086 -size_t pgc_aral_structures(void) {
2087 - return aral_structures_from_stats(&aral_statistics_for_pgc);
2088 -}
2089 -
2090 -size_t pgc_aral_overhead(void) {
2091 - return aral_overhead_from_stats(&aral_statistics_for_pgc);
2104 +struct aral_statistics *pgc_aral_stats(void) {
2105 + return &pgc_aral_statistics;
2106 }
2107
2108 void pgc_flush_all_hot_and_dirty_pages(PGC *cache, Word_t section) {
@@ -2455,6 +2469,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2469
2470 if(!page_acquire(cache, page)) {
2471 internal_fatal(true, "Migration to journal v2: cannot acquire page for migration to v2");
2472 + page_transition_unlock(cache, page);
2473 continue;
2474 }
2475
@@ -2561,8 +2576,17 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2576 Word_t start_time = 0;
2577 while ((PValue2 = JudyLFirstThenNext(mi->JudyL_pages_by_start_time, &start_time, &start_time_first))) {
2578 struct jv2_page_info *pi = *PValue2;
2579 +
2580 + // balance-parents: transition from hot to clean directly
2581 + page_set_clean(cache, pi->page, true, false);
2582 page_transition_unlock(cache, pi->page);
2565 - pgc_page_hot_to_dirty_and_release(cache, pi->page, true);
2583 + page_release(cache, pi->page, true);
2584 +
2585 + // before balance-parents:
2586 + // page_transition_unlock(cache, pi->page);
2587 + // pgc_page_hot_to_dirty_and_release(cache, pi->page, true);
2588 +
2589 + // old test - don't enable:
2590 // make_acquired_page_clean_and_evict_or_page_release(cache, pi->page);
2591 aral_freez(ar_pi, pi);
2592 }
@@ -2590,7 +2614,8 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2614
2615 __atomic_sub_fetch(&cache->stats.workers_jv2_flush, 1, __ATOMIC_RELAXED);
2616
2593 - flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, false, false);
2617 + // balance-parents: do not flush, there is nothing dirty
2618 + // flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, false, false);
2619 }
2620
2621 static bool match_page_data(PGC_PAGE *page, void *data) {
src/database/engine/cache.h
+129 -66
@@ -48,116 +48,180 @@ struct pgc_size_histogram {
48 };
49
50 struct pgc_queue_statistics {
51 + CACHE_LINE_PADDING();
52 struct pgc_size_histogram size_histogram;
53
53 - alignas(64) size_t entries;
54 - alignas(64) size_t size;
54 + CACHE_LINE_PADDING();
55 + size_t entries;
56 + CACHE_LINE_PADDING();
57 + size_t size;
58
56 - alignas(64) size_t max_entries;
57 - alignas(64) size_t max_size;
59 + CACHE_LINE_PADDING();
60 + size_t max_entries;
61 + CACHE_LINE_PADDING();
62 + size_t max_size;
63
59 - alignas(64) size_t added_entries;
60 - alignas(64) size_t added_size;
64 + CACHE_LINE_PADDING();
65 + size_t added_entries;
66 + CACHE_LINE_PADDING();
67 + size_t added_size;
68
62 - alignas(64) size_t removed_entries;
63 - alignas(64) size_t removed_size;
69 + CACHE_LINE_PADDING();
70 + size_t removed_entries;
71 + CACHE_LINE_PADDING();
72 + size_t removed_size;
73 +
74 + CACHE_LINE_PADDING();
75 };
76
77 struct pgc_statistics {
67 - alignas(64) size_t wanted_cache_size;
68 - alignas(64) size_t current_cache_size;
78 + CACHE_LINE_PADDING();
79 + size_t wanted_cache_size;
80 + CACHE_LINE_PADDING();
81 + size_t current_cache_size;
82 + CACHE_LINE_PADDING();
83
84 // ----------------------------------------------------------------------------------------------------------------
85 // volume
86
73 - alignas(64) size_t entries; // all the entries (includes clean, dirty, hot)
74 - alignas(64) size_t size; // all the entries (includes clean, dirty, hot)
87 + CACHE_LINE_PADDING();
88 + size_t entries; // all the entries (includes clean, dirty, hot)
89 + CACHE_LINE_PADDING();
90 + size_t size; // all the entries (includes clean, dirty, hot)
91
76 - alignas(64) size_t referenced_entries; // all the entries currently referenced
77 - alignas(64) size_t referenced_size; // all the entries currently referenced
92 + CACHE_LINE_PADDING();
93 + size_t referenced_entries; // all the entries currently referenced
94 + CACHE_LINE_PADDING();
95 + size_t referenced_size; // all the entries currently referenced
96
79 - alignas(64) size_t added_entries;
80 - alignas(64) size_t added_size;
97 + CACHE_LINE_PADDING();
98 + size_t added_entries;
99 + CACHE_LINE_PADDING();
100 + size_t added_size;
101
82 - alignas(64) size_t removed_entries;
83 - alignas(64) size_t removed_size;
102 + CACHE_LINE_PADDING();
103 + size_t removed_entries;
104 + CACHE_LINE_PADDING();
105 + size_t removed_size;
106
107 #ifdef PGC_COUNT_POINTS_COLLECTED
86 - alignas(64) size_t points_collected;
108 + CACHE_LINE_PADDING();
109 + size_t points_collected;
110 #endif
111
112 // ----------------------------------------------------------------------------------------------------------------
113 // migrations
114
92 - alignas(64) size_t evicting_entries;
93 - alignas(64) size_t evicting_size;
115 + CACHE_LINE_PADDING();
116 + size_t evicting_entries;
117 + CACHE_LINE_PADDING();
118 + size_t evicting_size;
119
95 - alignas(64) size_t flushing_entries;
96 - alignas(64) size_t flushing_size;
120 + CACHE_LINE_PADDING();
121 + size_t flushing_entries;
122 + CACHE_LINE_PADDING();
123 + size_t flushing_size;
124
98 - alignas(64) size_t hot2dirty_entries;
99 - alignas(64) size_t hot2dirty_size;
125 + CACHE_LINE_PADDING();
126 + size_t hot2dirty_entries;
127 + CACHE_LINE_PADDING();
128 + size_t hot2dirty_size;
129
101 - alignas(64) size_t hot_empty_pages_evicted_immediately;
102 - alignas(64) size_t hot_empty_pages_evicted_later;
130 + CACHE_LINE_PADDING();
131 + size_t hot_empty_pages_evicted_immediately;
132 + CACHE_LINE_PADDING();
133 + size_t hot_empty_pages_evicted_later;
134
135 // ----------------------------------------------------------------------------------------------------------------
136 // workload
137
107 - alignas(64) size_t acquires;
108 - alignas(64) size_t releases;
109 -
110 - alignas(64) size_t acquires_for_deletion;
111 -
112 - alignas(64) size_t searches_exact;
113 - alignas(64) size_t searches_exact_hits;
114 - alignas(64) size_t searches_exact_misses;
115 -
116 - alignas(64) size_t searches_closest;
117 - alignas(64) size_t searches_closest_hits;
118 - alignas(64) size_t searches_closest_misses;
119 -
120 - alignas(64) size_t flushes_completed;
121 - alignas(64) size_t flushes_completed_size;
122 - alignas(64) size_t flushes_cancelled_size;
138 + CACHE_LINE_PADDING();
139 + size_t acquires;
140 + CACHE_LINE_PADDING();
141 + size_t releases;
142 +
143 + CACHE_LINE_PADDING();
144 + size_t acquires_for_deletion;
145 +
146 + CACHE_LINE_PADDING();
147 + size_t searches_exact;
148 + CACHE_LINE_PADDING();
149 + size_t searches_exact_hits;
150 + CACHE_LINE_PADDING();
151 + size_t searches_exact_misses;
152 +
153 + CACHE_LINE_PADDING();
154 + size_t searches_closest;
155 + CACHE_LINE_PADDING();
156 + size_t searches_closest_hits;
157 + CACHE_LINE_PADDING();
158 + size_t searches_closest_misses;
159 +
160 + CACHE_LINE_PADDING();
161 + size_t flushes_completed;
162 + CACHE_LINE_PADDING();
163 + size_t flushes_completed_size;
164 + CACHE_LINE_PADDING();
165 + size_t flushes_cancelled_size;
166
167 // ----------------------------------------------------------------------------------------------------------------
168 // critical events
169
127 - alignas(64) size_t events_cache_under_severe_pressure;
128 - alignas(64) size_t events_cache_needs_space_aggressively;
129 - alignas(64) size_t events_flush_critical;
170 + CACHE_LINE_PADDING();
171 + size_t events_cache_under_severe_pressure;
172 + CACHE_LINE_PADDING();
173 + size_t events_cache_needs_space_aggressively;
174 + CACHE_LINE_PADDING();
175 + size_t events_flush_critical;
176
177 // ----------------------------------------------------------------------------------------------------------------
178 // worker threads
179
134 - alignas(64) size_t workers_search;
135 - alignas(64) size_t workers_add;
136 - alignas(64) size_t workers_evict;
137 - alignas(64) size_t workers_flush;
138 - alignas(64) size_t workers_jv2_flush;
139 - alignas(64) size_t workers_hot2dirty;
180 + CACHE_LINE_PADDING();
181 + size_t workers_search;
182 + CACHE_LINE_PADDING();
183 + size_t workers_add;
184 + CACHE_LINE_PADDING();
185 + size_t workers_evict;
186 + CACHE_LINE_PADDING();
187 + size_t workers_flush;
188 + CACHE_LINE_PADDING();
189 + size_t workers_jv2_flush;
190 + CACHE_LINE_PADDING();
191 + size_t workers_hot2dirty;
192
193 // ----------------------------------------------------------------------------------------------------------------
194 // waste events
195
196 // waste events - spins
145 - alignas(64) size_t waste_insert_spins;
146 - alignas(64) size_t waste_evict_useless_spins;
147 - alignas(64) size_t waste_release_spins;
148 - alignas(64) size_t waste_acquire_spins;
149 - alignas(64) size_t waste_delete_spins;
197 + CACHE_LINE_PADDING();
198 + size_t waste_insert_spins;
199 + CACHE_LINE_PADDING();
200 + size_t waste_evict_useless_spins;
201 + CACHE_LINE_PADDING();
202 + size_t waste_release_spins;
203 + CACHE_LINE_PADDING();
204 + size_t waste_acquire_spins;
205 + CACHE_LINE_PADDING();
206 + size_t waste_delete_spins;
207
208 // waste events - eviction
152 - alignas(64) size_t waste_evict_relocated;
153 - alignas(64) size_t waste_evict_thread_signals;
154 - alignas(64) size_t waste_evictions_inline_on_add;
155 - alignas(64) size_t waste_evictions_inline_on_release;
209 + CACHE_LINE_PADDING();
210 + size_t waste_evict_relocated;
211 + CACHE_LINE_PADDING();
212 + size_t waste_evict_thread_signals;
213 + CACHE_LINE_PADDING();
214 + size_t waste_evictions_inline_on_add;
215 + CACHE_LINE_PADDING();
216 + size_t waste_evictions_inline_on_release;
217
218 // waste events - flushing
158 - alignas(64) size_t waste_flush_on_add;
159 - alignas(64) size_t waste_flush_on_release;
160 - alignas(64) size_t waste_flushes_cancelled;
219 + CACHE_LINE_PADDING();
220 + size_t waste_flush_on_add;
221 + CACHE_LINE_PADDING();
222 + size_t waste_flush_on_release;
223 + CACHE_LINE_PADDING();
224 + size_t waste_flushes_cancelled;
225
226 // ----------------------------------------------------------------------------------------------------------------
227 // per queue statistics
@@ -248,8 +312,7 @@ bool pgc_flush_pages(PGC *cache);
312 struct pgc_statistics pgc_get_statistics(PGC *cache);
313 size_t pgc_hot_and_dirty_entries(PGC *cache);
314
251 -size_t pgc_aral_structures(void);
252 -size_t pgc_aral_overhead(void);
315 +struct aral_statistics *pgc_aral_stats(void);
316
317 static inline size_t indexing_partition(Word_t ptr, Word_t modulo) __attribute__((const));
318 static inline size_t indexing_partition(Word_t ptr, Word_t modulo) {
src/database/engine/metric.c
+26 -25
@@ -90,19 +90,16 @@ 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, size_t mem_before_judyl, size_t mem_after_judyl, size_t partition) {
94 - if(mem_after_judyl > mem_before_judyl)
95 - __atomic_add_fetch(&mrg->index[partition].stats.size, mem_after_judyl - mem_before_judyl, __ATOMIC_RELAXED);
96 - else if(mem_after_judyl < mem_before_judyl)
97 - __atomic_sub_fetch(&mrg->index[partition].stats.size, mem_before_judyl - mem_after_judyl, __ATOMIC_RELAXED);
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
100 -static inline void mrg_stats_size_judyhs_added_uuid(MRG *mrg, size_t partition) {
101 - __atomic_add_fetch(&mrg->index[partition].stats.size, JUDYHS_INDEX_SIZE_ESTIMATE(sizeof(nd_uuid_t)), __ATOMIC_RELAXED);
97 +static inline void mrg_stats_size_judyhs_added_uuid(MRG *mrg, size_t partition, int64_t judy_mem) {
98 + __atomic_add_fetch(&mrg->index[partition].stats.size, judy_mem, __ATOMIC_RELAXED);
99 }
100
104 -static inline void mrg_stats_size_judyhs_removed_uuid(MRG *mrg, size_t partition) {
105 - __atomic_sub_fetch(&mrg->index[partition].stats.size, JUDYHS_INDEX_SIZE_ESTIMATE(sizeof(nd_uuid_t)), __ATOMIC_RELAXED);
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) {
@@ -163,7 +160,7 @@ static inline bool acquired_metric_has_retention(MRG *mrg, METRIC *metric) {
160 static inline void acquired_for_deletion_metric_delete(MRG *mrg, METRIC *metric) {
161 size_t partition = metric->partition;
162
166 - size_t mem_before_judyl, mem_after_judyl;
163 + int64_t judy_mem;
164
165 mrg_index_write_lock(mrg, partition);
166
@@ -174,10 +171,10 @@ static inline void acquired_for_deletion_metric_delete(MRG *mrg, METRIC *metric)
171 return;
172 }
173
177 - mem_before_judyl = JudyLMemUsed(*sections_judy_pptr);
174 + judy_mem = -(int64_t)JudyLMemUsed(*sections_judy_pptr);
175 int rc = JudyLDel(sections_judy_pptr, metric->section, PJE0);
179 - mem_after_judyl = JudyLMemUsed(*sections_judy_pptr);
180 - mrg_stats_size_judyl_change(mrg, mem_before_judyl, mem_after_judyl, partition);
176 + judy_mem += (int64_t)JudyLMemUsed(*sections_judy_pptr);
177 + mrg_stats_size_judyl_change(mrg, judy_mem, partition);
178
179 if(unlikely(!rc)) {
180 MRG_STATS_DELETE_MISS(mrg, partition);
@@ -186,10 +183,15 @@ static inline void acquired_for_deletion_metric_delete(MRG *mrg, METRIC *metric)
183 }
184
185 if(!*sections_judy_pptr) {
186 + JudyAllocThreadPulseReset();
187 +
188 rc = JudyHSDel(&mrg->index[partition].uuid_judy, &metric->uuid, sizeof(nd_uuid_t), PJE0);
189 +
190 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
191 +
192 if(unlikely(!rc))
193 fatal("DBENGINE METRIC: cannot delete UUID from JudyHS");
192 - mrg_stats_size_judyhs_removed_uuid(mrg, partition);
194 + mrg_stats_size_judyhs_removed_uuid(mrg, partition, judy_mem);
195 }
196
197 MRG_STATS_DELETED_METRIC(mrg, partition);
@@ -262,19 +264,22 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r
264 while(1) {
265 mrg_index_write_lock(mrg, partition);
266
265 - size_t mem_before_judyl, mem_after_judyl;
267 + JudyAllocThreadPulseReset();
268
269 Pvoid_t *sections_judy_pptr = JudyHSIns(&mrg->index[partition].uuid_judy, entry->uuid, sizeof(nd_uuid_t), PJE0);
270 +
271 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
272 +
273 if (unlikely(!sections_judy_pptr || sections_judy_pptr == PJERR))
274 fatal("DBENGINE METRIC: corrupted UUIDs JudyHS array");
275
276 if (unlikely(!*sections_judy_pptr))
272 - mrg_stats_size_judyhs_added_uuid(mrg, partition);
277 + mrg_stats_size_judyhs_added_uuid(mrg, partition, judy_mem);
278
274 - mem_before_judyl = JudyLMemUsed(*sections_judy_pptr);
279 + judy_mem = -(int64_t)JudyLMemUsed(*sections_judy_pptr);
280 PValue = JudyLIns(sections_judy_pptr, entry->section, PJE0);
276 - mem_after_judyl = JudyLMemUsed(*sections_judy_pptr);
277 - mrg_stats_size_judyl_change(mrg, mem_before_judyl, mem_after_judyl, partition);
281 + judy_mem += (int64_t)JudyLMemUsed(*sections_judy_pptr);
282 + mrg_stats_size_judyl_change(mrg, judy_mem, partition);
283
284 if (unlikely(!PValue || PValue == PJERR))
285 fatal("DBENGINE METRIC: corrupted section JudyL array");
@@ -380,12 +385,8 @@ inline MRG *mrg_create(ssize_t partitions) {
385 return mrg;
386 }
387
383 -inline size_t mrg_aral_structures(void) {
384 - return aral_structures_from_stats(&mrg_aral_statistics);
385 -}
386 -
387 -inline size_t mrg_aral_overhead(void) {
388 - return aral_overhead_from_stats(&mrg_aral_statistics);
388 +struct aral_statistics *mrg_aral_stats(void) {
389 + return &mrg_aral_statistics;
390 }
391
392 inline void mrg_destroy(MRG *mrg __maybe_unused) {
src/database/engine/metric.h
+8 -11
@@ -4,8 +4,6 @@
4
5 #include "../rrd.h"
6
7 -#define MRG_CACHE_LINE_PADDING(x) uint8_t padding##x[64]
8 -
7 typedef struct metric METRIC;
8 typedef struct mrg MRG;
9
@@ -21,7 +19,7 @@ struct mrg_statistics {
19 // --- non-atomic --- under a write lock
20
21 size_t entries;
24 - size_t size; // total memory used, with indexing
22 + ssize_t size; // total memory used, with indexing
23
24 size_t additions;
25 size_t additions_duplicate;
@@ -30,21 +28,22 @@ struct mrg_statistics {
28 size_t delete_having_retention_or_referenced;
29 size_t delete_misses;
30
33 - MRG_CACHE_LINE_PADDING(0);
34 -
31 // --- atomic --- multiple readers / writers
32
33 + CACHE_LINE_PADDING();
34 size_t entries_referenced;
35
39 - MRG_CACHE_LINE_PADDING(2);
36 + CACHE_LINE_PADDING();
37 size_t current_references;
38
42 - MRG_CACHE_LINE_PADDING(3);
39 + CACHE_LINE_PADDING();
40 size_t search_hits;
41 + CACHE_LINE_PADDING();
42 size_t search_misses;
43
46 - MRG_CACHE_LINE_PADDING(4);
44 + CACHE_LINE_PADDING();
45 size_t writers;
46 + CACHE_LINE_PADDING();
47 size_t writers_conflicts;
48 };
49
@@ -83,9 +82,7 @@ bool mrg_metric_set_writer(MRG *mrg, METRIC *metric);
82 bool mrg_metric_clear_writer(MRG *mrg, METRIC *metric);
83
84 void mrg_get_statistics(MRG *mrg, struct mrg_statistics *s);
86 -size_t mrg_aral_structures(void);
87 -size_t mrg_aral_overhead(void);
88 -
85 +struct aral_statistics *mrg_aral_stats(void);
86
87 void mrg_update_metric_retention_and_granularity_by_uuid(
88 MRG *mrg, Word_t section, nd_uuid_t *uuid,
src/database/engine/page.c
+20 -8
@@ -62,6 +62,7 @@ struct pgd {
62 #define PGD_ARAL_PARTITIONS_MAX 256
63
64 struct {
65 + int64_t padding_used;
66 size_t partitions;
67
68 size_t sizeof_pgd;
@@ -77,7 +78,7 @@ struct {
78 #error "You need to update the slots reserved for storage tiers"
79 #endif
80
80 -static struct aral_statistics aral_statistics_for_pgd = { 0 };
81 +static struct aral_statistics pgd_aral_statistics = { 0 };
82
83 static size_t aral_sizes_delta;
84 static size_t aral_sizes_count;
@@ -89,8 +90,11 @@ static size_t aral_sizes[] = {
90 [RRD_STORAGE_TIERS - 2] = 0,
91 [RRD_STORAGE_TIERS - 1] = 0,
92
92 - // gorilla buffer size
93 + // gorilla buffer sizes
94 RRDENG_GORILLA_32BIT_BUFFER_SIZE,
95 + RRDENG_GORILLA_32BIT_BUFFER_SIZE * 2,
96 + RRDENG_GORILLA_32BIT_BUFFER_SIZE * 3,
97 + RRDENG_GORILLA_32BIT_BUFFER_SIZE * 4,
98
99 // our structures
100 sizeof(gorilla_writer_t),
@@ -101,12 +105,13 @@ static ARAL **arals = NULL;
105 #define arals_slot(slot, partition) ((partition) * aral_sizes_count + (slot))
106 static ARAL *pgd_get_aral_by_size_and_partition(size_t size, size_t partition);
107
104 -size_t pgd_aral_structures(void) {
105 - return aral_structures(pgd_alloc_globals.aral_pgd[0]);
108 +size_t pgd_padding_bytes(void) {
109 + int64_t x = __atomic_load_n(&pgd_alloc_globals.padding_used, __ATOMIC_RELAXED);
110 + return (x > 0) ? x : 0;
111 }
112
108 -size_t pgd_aral_overhead(void) {
109 - return aral_overhead(pgd_alloc_globals.aral_pgd[0]);
113 +struct aral_statistics *pgd_aral_stats(void) {
114 + return &pgd_aral_statistics;
115 }
116
117 int aral_size_sort_compare(const void *a, const void *b) {
@@ -175,7 +180,7 @@ void pgd_init_arals(void) {
180 aral_sizes[slot],
181 0,
182 0,
178 - &aral_statistics_for_pgd,
183 + &pgd_aral_statistics,
184 NULL, NULL, false, false);
185 }
186 }
@@ -254,6 +259,9 @@ static inline PGD *pgd_alloc(bool for_collector) {
259 static inline void *pgd_data_alloc(size_t size, size_t partition, bool for_collector) {
260 ARAL *ar = pgd_get_aral_by_size_and_partition(size, partition);
261 if(ar) {
262 + int64_t padding = (int64_t)aral_requested_element_size(ar) - (int64_t)size;
263 + __atomic_add_fetch(&pgd_alloc_globals.padding_used, padding, __ATOMIC_RELAXED);
264 +
265 if(for_collector)
266 return aral_mallocz_marked(ar);
267 else
@@ -265,8 +273,12 @@ static inline void *pgd_data_alloc(size_t size, size_t partition, bool for_colle
273
274 static void pgd_data_free(void *page, size_t size, size_t partition) {
275 ARAL *ar = pgd_get_aral_by_size_and_partition(size, partition);
268 - if(ar)
276 + if(ar) {
277 + int64_t padding = (int64_t)aral_requested_element_size(ar) - (int64_t)size;
278 + __atomic_sub_fetch(&pgd_alloc_globals.padding_used, padding, __ATOMIC_RELAXED);
279 +
280 aral_freez(ar, page);
281 + }
282 else
283 freez(page);
284 timing_dbengine_evict_step(TIMING_STEP_DBENGINE_EVICT_FREE_MAIN_PGD_TIER1_ARAL);
src/database/engine/page.h
+2 -2
@@ -38,8 +38,8 @@ uint32_t pgd_memory_footprint(PGD *pg);
38 uint32_t pgd_capacity(PGD *pg);
39 uint32_t pgd_disk_footprint(PGD *pg);
40
41 -size_t pgd_aral_structures(void);
42 -size_t pgd_aral_overhead(void);
41 +struct aral_statistics *pgd_aral_stats(void);
42 +size_t pgd_padding_bytes(void);
43
44 void pgd_copy_to_extent(PGD *pg, uint8_t *dst, uint32_t dst_size);
45
src/database/engine/pagecache.c
+9 -9
@@ -1033,7 +1033,7 @@ void pgc_open_add_hot_page(Word_t section, Word_t metric_id, time_t start_time_s
1033
1034 size_t dynamic_open_cache_size(void) {
1035 size_t main_wanted_cache_size = pgc_get_wanted_cache_size(main_cache);
1036 - size_t target_size = main_wanted_cache_size / 100 * 10; // 10%
1036 + size_t target_size = main_wanted_cache_size / 100 * 5;
1037
1038 if(target_size < 2 * 1024 * 1024)
1039 target_size = 2 * 1024 * 1024;
@@ -1048,7 +1048,7 @@ size_t dynamic_open_cache_size(void) {
1048
1049 size_t dynamic_extent_cache_size(void) {
1050 size_t main_wanted_cache_size = pgc_get_wanted_cache_size(main_cache);
1051 - size_t target_size = main_wanted_cache_size / 100 * 10; // 10%
1051 + size_t target_size = main_wanted_cache_size / 100 * 30;
1052
1053 if(target_size < 5 * 1024 * 1024)
1054 target_size = 5 * 1024 * 1024;
@@ -1070,12 +1070,12 @@ void pgc_and_mrg_initialize(void)
1070 main_mrg = mrg_create(0);
1071
1072 size_t target_cache_size = (size_t)default_rrdeng_page_cache_mb * 1024ULL * 1024ULL;
1073 - size_t main_cache_size = (target_cache_size / 100) * 95;
1073 + size_t main_cache_size = (target_cache_size / 100) * 70;
1074 size_t open_cache_size = 0;
1075 - size_t extent_cache_size = (target_cache_size / 100) * 5;
1075 + size_t extent_cache_size = (target_cache_size / 100) * 30;
1076
1077 - if(extent_cache_size < 3 * 1024 * 1024) {
1078 - extent_cache_size = 3 * 1024 * 1024;
1077 + if(extent_cache_size < 5 * 1024 * 1024) {
1078 + extent_cache_size = 5 * 1024 * 1024;
1079 main_cache_size = target_cache_size - extent_cache_size;
1080 }
1081
@@ -1092,7 +1092,7 @@ void pgc_and_mrg_initialize(void)
1092 pgc_max_evictors(),
1093 1000,
1094 1,
1095 - PGC_OPTIONS_AUTOSCALE,
1095 + PGC_OPTIONS_AUTOSCALE | PGC_OPTIONS_EVICT_PAGES_NO_INLINE,
1096 0,
1097 0
1098 );
@@ -1109,7 +1109,7 @@ void pgc_and_mrg_initialize(void)
1109 pgc_max_evictors(),
1110 1000,
1111 1,
1112 - PGC_OPTIONS_AUTOSCALE, // flushing inline: all dirty pages are just converted to clean
1112 + PGC_OPTIONS_AUTOSCALE | PGC_OPTIONS_FLUSH_PAGES_NO_INLINE | PGC_OPTIONS_EVICT_PAGES_NO_INLINE,
1113 0,
1114 sizeof(struct extent_io_data)
1115 );
@@ -1126,7 +1126,7 @@ void pgc_and_mrg_initialize(void)
1126 pgc_max_evictors(),
1127 1000,
1128 1,
1129 - PGC_OPTIONS_AUTOSCALE | PGC_OPTIONS_FLUSH_PAGES_NO_INLINE, // no flushing needed
1129 + PGC_OPTIONS_AUTOSCALE | PGC_OPTIONS_FLUSH_PAGES_NO_INLINE | PGC_OPTIONS_EVICT_PAGES_NO_INLINE, // no flushing needed
1130 0,
1131 0
1132 );
src/database/engine/pdc.c
+8 -8
@@ -71,8 +71,8 @@ static void pdc_release(PDC *pdc) {
71 aral_freez(pdc_globals.pdc.ar, pdc);
72 }
73
74 -size_t pdc_cache_size(void) {
75 - return aral_overhead(pdc_globals.pdc.ar) + aral_structures(pdc_globals.pdc.ar);
74 +struct aral_statistics *pdc_aral_stats(void) {
75 + return aral_get_statistics(pdc_globals.pdc.ar);
76 }
77
78 // ----------------------------------------------------------------------------
@@ -100,8 +100,8 @@ static void page_details_release(struct page_details *pd) {
100 aral_freez(pdc_globals.pd.ar, pd);
101 }
102
103 -size_t pd_cache_size(void) {
104 - return aral_overhead(pdc_globals.pd.ar) + aral_structures(pdc_globals.pd.ar);
103 +struct aral_statistics *pd_aral_stats(void) {
104 + return aral_get_statistics(pdc_globals.pd.ar);
105 }
106
107 // ----------------------------------------------------------------------------
@@ -129,8 +129,8 @@ static void epdl_release(EPDL *epdl) {
129 aral_freez(pdc_globals.epdl.ar, epdl);
130 }
131
132 -size_t epdl_cache_size(void) {
133 - return aral_overhead(pdc_globals.epdl.ar) + aral_structures(pdc_globals.epdl.ar);
132 +struct aral_statistics *epdl_aral_stats(void) {
133 + return aral_get_statistics(pdc_globals.epdl.ar);
134 }
135
136 // ----------------------------------------------------------------------------
@@ -159,8 +159,8 @@ static void deol_release(DEOL *deol) {
159 aral_freez(pdc_globals.deol.ar, deol);
160 }
161
162 -size_t deol_cache_size(void) {
163 - return aral_overhead(pdc_globals.deol.ar) + aral_structures(pdc_globals.deol.ar);
162 +struct aral_statistics *deol_aral_stats(void) {
163 + return aral_get_statistics(pdc_globals.deol.ar);
164 }
165
166 // ----------------------------------------------------------------------------
src/database/engine/pdc.h
+5 -4
@@ -34,10 +34,11 @@ typedef void (*execute_extent_page_details_list_t)(struct rrdengine_instance *ct
34 void pdc_to_epdl_router(struct rrdengine_instance *ctx, struct page_details_control *pdc, execute_extent_page_details_list_t exec_first_extent_list, execute_extent_page_details_list_t exec_rest_extent_list);
35 void epdl_find_extent_and_populate_pages(struct rrdengine_instance *ctx, EPDL *epdl, bool worker);
36
37 -size_t pdc_cache_size(void);
38 -size_t pd_cache_size(void);
39 -size_t epdl_cache_size(void);
40 -size_t deol_cache_size(void);
37 +struct aral_statistics *pdc_aral_stats(void);
38 +struct aral_statistics *pd_aral_stats(void);
39 +struct aral_statistics *epdl_aral_stats(void);
40 +struct aral_statistics *deol_aral_stats(void);
41 +
42 size_t extent_buffer_cache_size(void);
43
44 void pdc_init(void);
src/database/engine/rrdengine.c
+19 -21
@@ -5,11 +5,7 @@
5 #include "pdc.h"
6 #include "dbengine-compression.h"
7
8 -rrdeng_stats_t global_io_errors = 0;
9 -rrdeng_stats_t global_fs_errors = 0;
10 -rrdeng_stats_t rrdeng_reserved_file_descriptors = 0;
11 -rrdeng_stats_t global_pg_cache_over_half_dirty_events = 0;
12 -rrdeng_stats_t global_flushing_pressure_page_deletions = 0;
8 +struct rrdeng_global_stats global_stats = { 0 };
9
10 unsigned rrdeng_pages_per_extent = DEFAULT_PAGES_PER_EXTENT;
11
@@ -1587,25 +1583,27 @@ static void after_journal_v2_indexing(struct rrdengine_instance *ctx __maybe_unu
1583 rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1584 }
1585
1590 -struct rrdeng_buffer_sizes rrdeng_get_buffer_sizes(void) {
1586 +struct rrdeng_buffer_sizes rrdeng_pulse_memory_sizes(void) {
1587 return (struct rrdeng_buffer_sizes) {
1592 - .pgc = pgc_aral_overhead() + pgc_aral_structures(),
1593 - .pgd = pgd_aral_overhead() + pgd_aral_structures(),
1594 - .mrg = mrg_aral_overhead() + mrg_aral_structures(),
1595 - .opcodes = aral_overhead(rrdeng_main.cmd_queue.ar) + aral_structures(rrdeng_main.cmd_queue.ar),
1596 - .handles = aral_overhead(rrdeng_main.handles.ar) + aral_structures(rrdeng_main.handles.ar),
1597 - .descriptors = aral_overhead(rrdeng_main.descriptors.ar) + aral_structures(rrdeng_main.descriptors.ar),
1598 - .wal = __atomic_load_n(&wal_globals.atomics.allocated, __ATOMIC_RELAXED) * (sizeof(WAL) + RRDENG_BLOCK_SIZE),
1599 - .workers = aral_overhead(rrdeng_main.work_cmd.ar),
1600 - .pdc = pdc_cache_size(),
1601 - .xt_io = aral_overhead(rrdeng_main.xt_io_descr.ar) + aral_structures(rrdeng_main.xt_io_descr.ar),
1602 - .xt_buf = extent_buffer_cache_size(),
1603 - .epdl = epdl_cache_size(),
1604 - .deol = deol_cache_size(),
1605 - .pd = pd_cache_size(),
1588 + .as = {
1589 + [RRDENG_MEM_PGC] = pgc_aral_stats(),
1590 + [RRDENG_MEM_PGD] = pgd_aral_stats(),
1591 + [RRDENG_MEM_MRG] = mrg_aral_stats(),
1592 + [RRDENG_MEM_PDC] = pdc_aral_stats(),
1593 + [RRDENG_MEM_EPDL] = epdl_aral_stats(),
1594 + [RRDENG_MEM_DEOL] = deol_aral_stats(),
1595 + [RRDENG_MEM_PD] = pd_aral_stats(),
1596 + [RRDENG_MEM_OPCODES] = aral_get_statistics(rrdeng_main.cmd_queue.ar),
1597 + [RRDENG_MEM_HANDLES] = aral_get_statistics(rrdeng_main.handles.ar),
1598 + [RRDENG_MEM_DESCRIPTORS] = aral_get_statistics(rrdeng_main.descriptors.ar),
1599 + [RRDENG_MEM_WORKERS] = aral_get_statistics(rrdeng_main.work_cmd.ar),
1600 + [RRDENG_MEM_XT_IO] = aral_get_statistics(rrdeng_main.xt_io_descr.ar),
1601 + },
1602 + .wal = __atomic_load_n(&wal_globals.atomics.allocated, __ATOMIC_RELAXED) * (sizeof(WAL) + RRDENG_BLOCK_SIZE),
1603 + .xt_buf = extent_buffer_cache_size(),
1604
1605 #ifdef PDC_USE_JULYL
1608 - .julyl = julyl_cache_size(),
1606 + .julyl = julyl_cache_size(),
1607 #endif
1608 };
1609 }
src/database/engine/rrdengine.h
+50 -11
@@ -327,34 +327,60 @@ void wal_release(WAL *wal);
327 * They only describe operations since DB engine instance load time.
328 */
329 struct rrdengine_statistics {
330 + CACHE_LINE_PADDING();
331 rrdeng_stats_t before_decompress_bytes;
332 + CACHE_LINE_PADDING();
333 rrdeng_stats_t after_decompress_bytes;
334 + CACHE_LINE_PADDING();
335 rrdeng_stats_t before_compress_bytes;
336 + CACHE_LINE_PADDING();
337 rrdeng_stats_t after_compress_bytes;
338
339 + CACHE_LINE_PADDING();
340 rrdeng_stats_t io_write_bytes;
341 + CACHE_LINE_PADDING();
342 rrdeng_stats_t io_write_requests;
343 + CACHE_LINE_PADDING();
344 rrdeng_stats_t io_read_bytes;
345 + CACHE_LINE_PADDING();
346 rrdeng_stats_t io_read_requests;
347
348 + CACHE_LINE_PADDING();
349 rrdeng_stats_t datafile_creations;
350 + CACHE_LINE_PADDING();
351 rrdeng_stats_t datafile_deletions;
352 + CACHE_LINE_PADDING();
353 rrdeng_stats_t journalfile_creations;
354 + CACHE_LINE_PADDING();
355 rrdeng_stats_t journalfile_deletions;
356
357 + CACHE_LINE_PADDING();
358 rrdeng_stats_t io_errors;
359 + CACHE_LINE_PADDING();
360 rrdeng_stats_t fs_errors;
361 };
362
349 -/* I/O errors global counter */
350 -extern rrdeng_stats_t global_io_errors;
351 -/* File-System errors global counter */
352 -extern rrdeng_stats_t global_fs_errors;
353 -/* number of File-Descriptors that have been reserved by dbengine */
354 -extern rrdeng_stats_t rrdeng_reserved_file_descriptors;
355 -/* inability to flush global counters */
356 -extern rrdeng_stats_t global_pg_cache_over_half_dirty_events;
357 -extern rrdeng_stats_t global_flushing_pressure_page_deletions; /* number of deleted pages */
363 +struct rrdeng_global_stats {
364 + CACHE_LINE_PADDING();
365 + /* I/O errors global counter */
366 + rrdeng_stats_t global_io_errors;
367 +
368 + CACHE_LINE_PADDING();
369 + /* File-System errors global counter */
370 + rrdeng_stats_t global_fs_errors;
371 +
372 + CACHE_LINE_PADDING();
373 + /* number of File-Descriptors that have been reserved by dbengine */
374 + rrdeng_stats_t rrdeng_reserved_file_descriptors;
375 +
376 + CACHE_LINE_PADDING();
377 + /* inability to flush global counters */
378 + rrdeng_stats_t global_pg_cache_over_half_dirty_events;
379 + CACHE_LINE_PADDING();
380 + rrdeng_stats_t global_flushing_pressure_page_deletions; /* number of deleted pages */
381 +};
382 +
383 +extern struct rrdeng_global_stats global_stats;
384
385 typedef struct tier_config_prototype {
386 int tier; // the tier of this ctx
@@ -387,22 +413,35 @@ struct rrdengine_instance {
413 } njfv2idx;
414
415 struct {
416 + CACHE_LINE_PADDING();
417 unsigned last_fileno; // newest index of datafile and journalfile
418 + CACHE_LINE_PADDING();
419 unsigned last_flush_fileno; // newest index of datafile received data
420
421 + CACHE_LINE_PADDING();
422 size_t collectors_running;
423 + CACHE_LINE_PADDING();
424 size_t collectors_running_duplicate;
425 + CACHE_LINE_PADDING();
426 size_t inflight_queries; // the number of queries currently running
427 + CACHE_LINE_PADDING();
428 uint64_t current_disk_space; // the current disk space size used
429
430 + CACHE_LINE_PADDING();
431 uint64_t transaction_id; // the transaction id of the next extent flushing
432
433 + CACHE_LINE_PADDING();
434 bool migration_to_v2_running;
435 + CACHE_LINE_PADDING();
436 bool now_deleting_files;
437 + CACHE_LINE_PADDING();
438 unsigned extents_currently_being_flushed; // non-zero until we commit data to disk (both datafile and journal file)
439
440 + CACHE_LINE_PADDING();
441 time_t first_time_s;
442 + CACHE_LINE_PADDING();
443 uint64_t metrics;
444 + CACHE_LINE_PADDING();
445 uint64_t samples;
446 } atomic;
447
@@ -440,12 +479,12 @@ static inline void ctx_io_write_op_bytes(struct rrdengine_instance *ctx, size_t
479
480 static inline void ctx_io_error(struct rrdengine_instance *ctx) {
481 __atomic_add_fetch(&ctx->stats.io_errors, 1, __ATOMIC_RELAXED);
443 - rrd_stat_atomic_add(&global_io_errors, 1);
482 + rrd_stat_atomic_add(&global_stats.global_io_errors, 1);
483 }
484
485 static inline void ctx_fs_error(struct rrdengine_instance *ctx) {
486 __atomic_add_fetch(&ctx->stats.fs_errors, 1, __ATOMIC_RELAXED);
448 - rrd_stat_atomic_add(&global_fs_errors, 1);
487 + rrd_stat_atomic_add(&global_stats.global_fs_errors, 1);
488 }
489
490 #define ctx_last_fileno_get(ctx) __atomic_load_n(&(ctx)->atomic.last_fileno, __ATOMIC_RELAXED)
src/database/engine/rrdengineapi.c
+12 -12
@@ -1046,13 +1046,13 @@ void rrdeng_get_37_statistics(struct rrdengine_instance *ctx, unsigned long long
1046 array[27] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.page_cache_descriptors, __ATOMIC_RELAXED);
1047 array[28] = (uint64_t)__atomic_load_n(&ctx->stats.io_errors, __ATOMIC_RELAXED);
1048 array[29] = (uint64_t)__atomic_load_n(&ctx->stats.fs_errors, __ATOMIC_RELAXED);
1049 - array[30] = (uint64_t)__atomic_load_n(&global_io_errors, __ATOMIC_RELAXED); // used
1050 - array[31] = (uint64_t)__atomic_load_n(&global_fs_errors, __ATOMIC_RELAXED); // used
1051 - array[32] = (uint64_t)__atomic_load_n(&rrdeng_reserved_file_descriptors, __ATOMIC_RELAXED); // used
1049 + array[30] = (uint64_t)__atomic_load_n(&global_stats.global_io_errors, __ATOMIC_RELAXED); // used
1050 + array[31] = (uint64_t)__atomic_load_n(&global_stats.global_fs_errors, __ATOMIC_RELAXED); // used
1051 + array[32] = (uint64_t)__atomic_load_n(&global_stats.rrdeng_reserved_file_descriptors, __ATOMIC_RELAXED); // used
1052 array[33] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.pg_cache_over_half_dirty_events, __ATOMIC_RELAXED);
1053 - array[34] = (uint64_t)__atomic_load_n(&global_pg_cache_over_half_dirty_events, __ATOMIC_RELAXED); // used
1053 + array[34] = (uint64_t)__atomic_load_n(&global_stats.global_pg_cache_over_half_dirty_events, __ATOMIC_RELAXED); // used
1054 array[35] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.flushing_pressure_page_deletions, __ATOMIC_RELAXED);
1055 - array[36] = (uint64_t)__atomic_load_n(&global_flushing_pressure_page_deletions, __ATOMIC_RELAXED); // used
1055 + array[36] = (uint64_t)__atomic_load_n(&global_stats.global_flushing_pressure_page_deletions, __ATOMIC_RELAXED); // used
1056 array[37] = 0; //(uint64_t)pg_cache->active_descriptors;
1057
1058 fatal_assert(RRDENG_NR_STATS == 38);
@@ -1144,15 +1144,15 @@ int rrdeng_init(
1144 max_open_files = rlimit_nofile.rlim_cur / 4;
1145
1146 /* reserve RRDENG_FD_BUDGET_PER_INSTANCE file descriptors for this instance */
1147 - rrd_stat_atomic_add(&rrdeng_reserved_file_descriptors, RRDENG_FD_BUDGET_PER_INSTANCE);
1148 - if (rrdeng_reserved_file_descriptors > max_open_files) {
1147 + rrd_stat_atomic_add(&global_stats.rrdeng_reserved_file_descriptors, RRDENG_FD_BUDGET_PER_INSTANCE);
1148 + if (global_stats.rrdeng_reserved_file_descriptors > max_open_files) {
1149 netdata_log_error(
1150 "Exceeded the budget of available file descriptors (%u/%u), cannot create new dbengine instance.",
1151 - (unsigned)rrdeng_reserved_file_descriptors,
1151 + (unsigned)global_stats.rrdeng_reserved_file_descriptors,
1152 (unsigned)max_open_files);
1153
1154 - rrd_stat_atomic_add(&global_fs_errors, 1);
1155 - rrd_stat_atomic_add(&rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
1154 + rrd_stat_atomic_add(&global_stats.global_fs_errors, 1);
1155 + rrd_stat_atomic_add(&global_stats.rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
1156 return UV_EMFILE;
1157 }
1158
@@ -1196,7 +1196,7 @@ int rrdeng_init(
1196 *ctxp = NULL;
1197 }
1198
1199 - rrd_stat_atomic_add(&rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
1199 + rrd_stat_atomic_add(&global_stats.rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
1200 return UV_EIO;
1201 }
1202
@@ -1243,7 +1243,7 @@ int rrdeng_exit(struct rrdengine_instance *ctx) {
1243 if (unittest_running) //(ctx->config.unittest)
1244 freez(ctx);
1245
1246 - rrd_stat_atomic_add(&rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
1246 + rrd_stat_atomic_add(&global_stats.rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
1247 return 0;
1248 }
1249
src/database/engine/rrdengineapi.h
+21 -13
@@ -208,27 +208,35 @@ struct rrdeng_cache_efficiency_stats {
208 size_t metrics_retention_started;
209 };
210
211 +typedef enum rrdeng_mem {
212 + RRDENG_MEM_PGC = 0,
213 + RRDENG_MEM_PGD,
214 + RRDENG_MEM_MRG,
215 + RRDENG_MEM_OPCODES,
216 + RRDENG_MEM_HANDLES,
217 + RRDENG_MEM_DESCRIPTORS,
218 + RRDENG_MEM_WORKERS,
219 + RRDENG_MEM_PDC,
220 + RRDENG_MEM_XT_IO,
221 + RRDENG_MEM_EPDL,
222 + RRDENG_MEM_DEOL,
223 + RRDENG_MEM_PD,
224 +
225 + // terminator
226 + RRDENG_MEM_MAX,
227 +} RRDENG_MEM;
228 +
229 struct rrdeng_buffer_sizes {
212 - size_t workers;
213 - size_t pdc;
230 + struct aral_statistics *as[RRDENG_MEM_MAX];
231 +
232 size_t wal;
215 - size_t descriptors;
216 - size_t xt_io;
233 size_t xt_buf;
218 - size_t handles;
219 - size_t opcodes;
220 - size_t epdl;
221 - size_t deol;
222 - size_t pd;
223 - size_t pgc;
224 - size_t pgd;
225 - size_t mrg;
234 #ifdef PDC_USE_JULYL
235 size_t julyl;
236 #endif
237 };
238
231 -struct rrdeng_buffer_sizes rrdeng_get_buffer_sizes(void);
239 +struct rrdeng_buffer_sizes rrdeng_pulse_memory_sizes(void);
240 struct rrdeng_cache_efficiency_stats rrdeng_get_cache_efficiency_stats(void);
241
242 RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx);
src/database/rrd.h
+23 -4
@@ -278,7 +278,7 @@ struct rrddim_tier {
278 STORAGE_COLLECT_HANDLE *sch; // the data collection handle
279 };
280
281 -void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s);
281 +void backfill_tier_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s);
282
283 // ----------------------------------------------------------------------------
284 // RRD DIMENSION - this is a metric
@@ -921,8 +921,23 @@ typedef enum __attribute__ ((__packed__)) rrdhost_flags {
921 // Careful not to overlap with rrdhost_options to avoid bugs if
922 // rrdhost_flags_xxx is used instead of rrdhost_option_xxx or vice-versa
923 // Orphan, Archived and Obsolete flags
924 +
925 + /*
926 + * 3 BASE FLAGS FOR HOSTS:
927 + *
928 + * - COLLECTOR_ONLINE = the collector is currently collecting data for this node
929 + * this is true FOR ALL KINDS OF NODES (including localhost, virtual hosts, children)
930 + *
931 + * - ORPHAN = the node had a collector online recently, but does not have it now
932 + *
933 + * - ARCHIVED = the node does not have data collection structures attached to it
934 + *
935 + */
936 +
937 + RRDHOST_FLAG_COLLECTOR_ONLINE = (1 << 7), // the collector of this host is online
938 RRDHOST_FLAG_ORPHAN = (1 << 8), // this host is orphan (not receiving data)
939 RRDHOST_FLAG_ARCHIVED = (1 << 9), // The host is archived, no collected charts yet
940 +
941 RRDHOST_FLAG_PENDING_OBSOLETE_CHARTS = (1 << 10), // the host has pending chart obsoletions
942 RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS = (1 << 11), // the host has pending dimension obsoletions
943
@@ -951,7 +966,6 @@ typedef enum __attribute__ ((__packed__)) rrdhost_flags {
966 RRDHOST_FLAG_PENDING_CONTEXT_LOAD = (1 << 26), // Context needs to be loaded
967
968 RRDHOST_FLAG_METADATA_CLAIMID = (1 << 27), // metadata needs to be stored in the database
954 - RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED = (1 << 28), // set when the receiver part is disconnected
969
970 RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED = (1 << 29), // set when the host has updated global functions
971 } RRDHOST_FLAGS;
@@ -990,7 +1004,7 @@ typedef enum __attribute__ ((__packed__)) {
1004 #define rrdhost_can_stream_metadata_to_parent(host) \
1005 (rrdhost_has_stream_sender_enabled(host) && \
1006 rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS) && \
993 - !rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED) \
1007 + rrdhost_flag_check(host, RRDHOST_FLAG_COLLECTOR_ONLINE) \
1008 )
1009
1010 // ----------------------------------------------------------------------------
@@ -1358,7 +1372,12 @@ extern RRDHOST *localhost;
1372 #define rrdhost_sender_replicating_charts_minus_one(host) (__atomic_sub_fetch(&((host)->stream.snd.status.replication.charts), 1, __ATOMIC_RELAXED))
1373 #define rrdhost_sender_replicating_charts_zero(host) (__atomic_store_n(&((host)->stream.snd.status.replication.charts), 0, __ATOMIC_RELAXED))
1374
1361 -#define rrdhost_is_online(host) ((host) == localhost || rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST) || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN | RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED))
1375 +#define rrdhost_is_online(host) ( \
1376 + (host) == localhost || \
1377 + rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST) || \
1378 + (rrdhost_flag_check(host, RRDHOST_FLAG_COLLECTOR_ONLINE) && !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)) \
1379 + )
1380 +
1381 bool rrdhost_matches_window(RRDHOST *host, time_t after, time_t before, time_t now);
1382
1383 extern DICTIONARY *rrdhost_root_index;
src/database/rrdhost.c
+3
@@ -841,6 +841,9 @@ int rrd_init(const char *hostname, struct rrdhost_system_info *system_info, bool
841 if (unlikely(!localhost))
842 return 1;
843
844 + rrdhost_flag_set(localhost, RRDHOST_FLAG_COLLECTOR_ONLINE);
845 +
846 + ml_host_start(localhost);
847 dyncfg_host_init(localhost);
848
849 if(!unittest)
src/database/rrdlabels.c
+20 -9
@@ -65,9 +65,9 @@ typedef struct rrdlabels {
65 } \
66 while (0)
67
68 -static inline void STATS_PLUS_MEMORY(struct dictionary_stats *stats, size_t key_size, size_t item_size, size_t value_size) {
69 - if(key_size)
70 - __atomic_fetch_add(&stats->memory.index, (long)JUDYHS_INDEX_SIZE_ESTIMATE(key_size), __ATOMIC_RELAXED);
68 +static inline void STATS_PLUS_MEMORY(struct dictionary_stats *stats, int64_t judy_mem, size_t item_size, size_t value_size) {
69 + if(judy_mem)
70 + __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
71
72 if(item_size)
73 __atomic_fetch_add(&stats->memory.dict, (long)item_size, __ATOMIC_RELAXED);
@@ -76,9 +76,9 @@ static inline void STATS_PLUS_MEMORY(struct dictionary_stats *stats, size_t key_
76 __atomic_fetch_add(&stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
77 }
78
79 -static inline void STATS_MINUS_MEMORY(struct dictionary_stats *stats, size_t key_size, size_t item_size, size_t value_size) {
80 - if(key_size)
81 - __atomic_fetch_sub(&stats->memory.index, (long)JUDYHS_INDEX_SIZE_ESTIMATE(key_size), __ATOMIC_RELAXED);
79 +static inline void STATS_MINUS_MEMORY(struct dictionary_stats *stats, int64_t judy_mem, size_t item_size, size_t value_size) {
80 + if(judy_mem)
81 + __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
82
83 if(item_size)
84 __atomic_fetch_sub(&stats->memory.dict, (long)item_size, __ATOMIC_RELAXED);
@@ -131,7 +131,12 @@ static RRDLABEL *add_label_name_value(const char *name, const char *value)
131
132 spinlock_lock(&global_labels.spinlock);
133
134 + JudyAllocThreadPulseReset();
135 +
136 Pvoid_t *PValue = JudyHSIns(&global_labels.JudyHS, (void *)&label_index, sizeof(label_index), PJE0);
137 +
138 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
139 +
140 if(unlikely(!PValue || PValue == PJERR))
141 fatal("RRDLABELS: corrupted judyHS array");
142
@@ -139,11 +144,12 @@ static RRDLABEL *add_label_name_value(const char *name, const char *value)
144 rrdlabel = *PValue;
145 string_freez(label_index.key);
146 string_freez(label_index.value);
147 + STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, 0, 0);
148 } else {
149 rrdlabel = callocz(1, sizeof(*rrdlabel));
150 rrdlabel->label.index = label_index;
151 *PValue = rrdlabel;
146 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, sizeof(LABEL_REGISTRY_IDX), sizeof(RRDLABEL_IDX), 0);
152 + STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(RRDLABEL_IDX), 0);
153 }
154 __atomic_add_fetch(&rrdlabel->refcount, 1, __ATOMIC_RELAXED);
155
@@ -160,11 +166,16 @@ static void delete_label(RRDLABEL *label)
166 RRDLABEL_IDX *rrdlabel = *PValue;
167 size_t refcount = __atomic_sub_fetch(&rrdlabel->refcount, 1, __ATOMIC_RELAXED);
168 if (refcount == 0) {
169 + JudyAllocThreadPulseReset();
170 +
171 int ret = JudyHSDel(&global_labels.JudyHS, (void *)label, sizeof(*label), PJE0);
172 +
173 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
174 +
175 if (unlikely(ret == JERR))
165 - STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, sizeof(*rrdlabel), 0);
176 + STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(*rrdlabel), 0);
177 else
167 - STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, sizeof(LABEL_REGISTRY_IDX), sizeof(*rrdlabel), 0);
178 + STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(*rrdlabel), 0);
179 string_freez(label->index.key);
180 string_freez(label->index.value);
181 freez(rrdlabel);
src/database/rrdset.c
+1 -1
@@ -1281,7 +1281,7 @@ void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n,
1281 if(!rrddim_option_check(rd, RRDDIM_OPTION_BACKFILLED_HIGH_TIERS)) {
1282 // we have not collected this tier before
1283 // let's fill any gap that may exist
1284 - rrdr_fill_tier_gap_from_smaller_tiers(rd, tier, now_s);
1284 + backfill_tier_from_smaller_tiers(rd, tier, now_s);
1285 }
1286
1287 store_metric_at_tier(rd, tier, t, sp, point_end_time_ut);
src/health/health_event_loop.c
+6
@@ -213,6 +213,12 @@ static void health_event_loop(void) {
213 unsigned int loop = 0;
214
215 while(service_running(SERVICE_HEALTH)) {
216 + if(!stream_control_health_should_be_running()) {
217 + worker_is_idle();
218 + stream_control_throttle();
219 + continue;
220 + }
221 +
222 loop++;
223 netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
224
src/libnetdata/aral/aral.c
+94 -61
@@ -11,6 +11,12 @@
11 #define TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS
12 #endif
13
14 +#if ENV32BIT
15 +#define SYSTEM_REQUIRED_ALIGNMENT (sizeof(uintptr_t) * 2)
16 +#else
17 +#define SYSTEM_REQUIRED_ALIGNMENT (alignof(uintptr_t))
18 +#endif
19 +
20 // max mapped file size
21 #define ARAL_MAX_PAGE_SIZE_MMAP (1ULL * 1024 * 1024 * 1024)
22
@@ -61,13 +67,17 @@ typedef enum {
67
68 struct aral_ops {
69 struct {
64 - alignas(64) size_t allocators; // the number of threads currently trying to allocate memory
65 - alignas(64) size_t deallocators; // the number of threads currently trying to deallocate memory
66 - alignas(64) bool last_allocated_or_deallocated; // stability detector, true when was last allocated
70 + CACHE_LINE_PADDING();
71 + size_t allocators; // the number of threads currently trying to allocate memory
72 + CACHE_LINE_PADDING();
73 + size_t deallocators; // the number of threads currently trying to deallocate memory
74 + CACHE_LINE_PADDING();
75 + bool last_allocated_or_deallocated; // stability detector, true when was last allocated
76 } atomic;
77
78 struct {
70 - alignas(64) SPINLOCK spinlock;
79 + CACHE_LINE_PADDING();
80 + SPINLOCK spinlock;
81 size_t allocating_elements; // currently allocating elements
82 size_t allocation_size; // current / next allocation size
83 } adders;
@@ -97,7 +107,7 @@ struct aral {
107 } config;
108
109 struct {
100 - alignas(64) SPINLOCK spinlock;
110 + SPINLOCK spinlock;
111 size_t file_number; // for mmap
112
113 ARAL_PAGE *pages_free; // pages with free items
@@ -125,12 +135,12 @@ const char *aral_name(ARAL *ar) {
135 return ar->config.name;
136 }
137
128 -size_t aral_structures_from_stats(struct aral_statistics *stats) {
138 +size_t aral_structures_bytes_from_stats(struct aral_statistics *stats) {
139 if(!stats) return 0;
140 return __atomic_load_n(&stats->structures.allocated_bytes, __ATOMIC_RELAXED);
141 }
142
133 -size_t aral_overhead_from_stats(struct aral_statistics *stats) {
143 +size_t aral_free_bytes_from_stats(struct aral_statistics *stats) {
144 if(!stats) return 0;
145
146 size_t allocated = __atomic_load_n(&stats->malloc.allocated_bytes, __ATOMIC_RELAXED) +
@@ -139,23 +149,39 @@ size_t aral_overhead_from_stats(struct aral_statistics *stats) {
149 size_t used = __atomic_load_n(&stats->malloc.used_bytes, __ATOMIC_RELAXED) +
150 __atomic_load_n(&stats->mmap.used_bytes, __ATOMIC_RELAXED);
151
142 - if(allocated > used) return allocated - used;
143 - return allocated;
152 + return (allocated > used) ? allocated - used : 0;
153 }
154
155 size_t aral_used_bytes_from_stats(struct aral_statistics *stats) {
156 size_t used = __atomic_load_n(&stats->malloc.used_bytes, __ATOMIC_RELAXED) +
157 __atomic_load_n(&stats->mmap.used_bytes, __ATOMIC_RELAXED);
149 -
158 return used;
159 }
160
153 -size_t aral_overhead(ARAL *ar) {
154 - return aral_overhead_from_stats(ar->stats);
161 +size_t aral_padding_bytes_from_stats(struct aral_statistics *stats) {
162 + size_t padding = __atomic_load_n(&stats->malloc.padding_bytes, __ATOMIC_RELAXED) +
163 + __atomic_load_n(&stats->mmap.padding_bytes, __ATOMIC_RELAXED);
164 + return padding;
165 +}
166 +
167 +size_t aral_used_bytes(ARAL *ar) {
168 + return aral_used_bytes_from_stats(ar->stats);
169 }
170
157 -size_t aral_structures(ARAL *ar) {
158 - return aral_structures_from_stats(ar->stats);
171 +size_t aral_free_bytes(ARAL *ar) {
172 + return aral_free_bytes_from_stats(ar->stats);
173 +}
174 +
175 +size_t aral_structures_bytes(ARAL *ar) {
176 + return aral_structures_bytes_from_stats(ar->stats);
177 +}
178 +
179 +size_t aral_padding_bytes(ARAL *ar) {
180 + return aral_padding_bytes_from_stats(ar->stats);
181 +}
182 +
183 +size_t aral_free_structures_padding_from_stats(struct aral_statistics *stats) {
184 + return aral_free_bytes_from_stats(stats) + aral_structures_bytes_from_stats(stats) + aral_padding_bytes_from_stats(stats);
185 }
186
187 struct aral_statistics *aral_get_statistics(ARAL *ar) {
@@ -343,6 +369,8 @@ static ARAL_PAGE *aral_get_page_pointer_after_element___do_NOT_have_aral_lock(AR
369 }
370 #endif
371
372 + internal_fatal((uintptr_t)page % SYSTEM_REQUIRED_ALIGNMENT != 0, "Pointer is not aligned properly");
373 +
374 return page;
375 }
376
@@ -387,11 +415,6 @@ static size_t aral_get_system_page_size(void) {
415 return page_size;
416 }
417
390 -// we don't need alignof(max_align_t) for normal C structures
391 -// alignof(uintptr_r) is sufficient for our use cases
392 -// #define SYSTEM_REQUIRED_ALIGNMENT (alignof(max_align_t))
393 -#define SYSTEM_REQUIRED_ALIGNMENT (alignof(uintptr_t))
394 -
418 static size_t aral_element_slot_size(size_t requested_element_size, bool usable) {
419 // we need to add a page pointer after the element
420 // so, first align the element size to the pointer size
@@ -453,8 +476,11 @@ static size_t aral_next_allocation_size___adders_lock_needed(ARAL *ar, bool mark
476 // --------------------------------------------------------------------------------------------------------------------
477
478 static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
456 - size_t data_size, structures_size;
479 + struct aral_page_type_stats *stats;
480 ARAL_PAGE *page;
481 +
482 + size_t total_size = size;
483 +
484 if(ar->config.mmap.enabled) {
485 page = callocz(1, sizeof(ARAL_PAGE));
486 ar->aral_lock.file_number++;
@@ -469,10 +495,8 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
495 fatal("ARAL: '%s' cannot allocate aral buffer of size %zu on filename '%s'",
496 ar->config.name, size, page->filename);
497
472 - __atomic_add_fetch(&ar->stats->mmap.allocations, 1, __ATOMIC_RELAXED);
473 - __atomic_add_fetch(&ar->stats->mmap.allocated_bytes, size, __ATOMIC_RELAXED);
474 - data_size = size;
475 - structures_size = sizeof(ARAL_PAGE);
498 + total_size = size + sizeof(ARAL_PAGE);
499 + stats = &ar->stats->mmap;
500 }
501 #ifdef NETDATA_TRACE_ALLOCATIONS
502 else {
@@ -485,23 +509,18 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
509 #else
510 else {
511 size_t ARAL_PAGE_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT);
488 - size_t max_elements = aral_elements_in_page_size(ar, size);
489 - data_size = max_elements * ar->config.element_size;
490 - structures_size = size - data_size;
512
513 if (size >= ARAL_MMAP_PAGES_ABOVE) {
514 bool mapped;
515 uint8_t *ptr = netdata_mmap(NULL, size, MAP_PRIVATE, 1, false, NULL);
516 if (ptr) {
517 mapped = true;
497 - __atomic_add_fetch(&ar->stats->mmap.allocations, 1, __ATOMIC_RELAXED);
498 - __atomic_add_fetch(&ar->stats->mmap.allocated_bytes, data_size, __ATOMIC_RELAXED);
518 + stats = &ar->stats->mmap;
519 }
520 else {
521 ptr = mallocz(size);
522 mapped = false;
503 - __atomic_add_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
504 - __atomic_add_fetch(&ar->stats->malloc.allocated_bytes, data_size, __ATOMIC_RELAXED);
523 + stats = &ar->stats->malloc;
524 }
525 page = (ARAL_PAGE *)ptr;
526 memset(page, 0, ARAL_PAGE_size);
@@ -515,8 +534,7 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
534 page->data = &ptr[ARAL_PAGE_size];
535 page->mapped = false;
536
518 - __atomic_add_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
519 - __atomic_add_fetch(&ar->stats->malloc.allocated_bytes, data_size, __ATOMIC_RELAXED);
537 + stats = &ar->stats->malloc;
538 }
539 }
540 #endif
@@ -526,13 +544,21 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
544 page->max_elements = aral_elements_in_page_size(ar, page->size);
545 page->aral_lock.free_elements = page->max_elements;
546
547 + size_t structures_size = sizeof(ARAL_PAGE) + page->max_elements * sizeof(void *);
548 + size_t data_size = page->max_elements * ar->config.requested_element_size;
549 + size_t padding_size = total_size - data_size - structures_size;
550 +
551 + __atomic_add_fetch(&stats->allocations, 1, __ATOMIC_RELAXED);
552 + __atomic_add_fetch(&stats->allocated_bytes, data_size, __ATOMIC_RELAXED);
553 + __atomic_add_fetch(&stats->padding_bytes, padding_size, __ATOMIC_RELAXED);
554 +
555 __atomic_add_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
556 __atomic_add_fetch(&ar->stats->structures.allocated_bytes, structures_size, __ATOMIC_RELAXED);
557
558 // link the free space to its page
559 ARAL_FREE *fr = (ARAL_FREE *)page->data;
560
535 - fr->size = data_size;
561 + fr->size = page->max_elements * ar->config.element_size;
562 fr->next = NULL;
563 page->free.list = fr;
564
@@ -545,15 +571,15 @@ void aral_del_page___no_lock_needed(ARAL *ar, ARAL_PAGE *page TRACE_ALLOCATIONS_
571 size_t idx = mark_to_idx(page->started_marked);
572 __atomic_store_n(&ar->ops[idx].atomic.last_allocated_or_deallocated, true, __ATOMIC_RELAXED);
573
548 - size_t data_size, structures_size;
574 + struct aral_page_type_stats *stats;
575 + size_t max_elements = page->max_elements;
576 + size_t size = page->size;
577 + size_t total_size = size;
578
579 // free it
580 if (ar->config.mmap.enabled) {
552 - data_size = page->size;
553 - structures_size = sizeof(ARAL_PAGE);
554 -
555 - __atomic_sub_fetch(&ar->stats->mmap.allocations, 1, __ATOMIC_RELAXED);
556 - __atomic_sub_fetch(&ar->stats->mmap.allocated_bytes, page->size, __ATOMIC_RELAXED);
581 + stats = &ar->stats->mmap;
582 + total_size = size + sizeof(ARAL_PAGE);
583
584 netdata_munmap(page->data, page->size);
585
@@ -571,24 +597,25 @@ void aral_del_page___no_lock_needed(ARAL *ar, ARAL_PAGE *page TRACE_ALLOCATIONS_
597 freez_int(page->data TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
598 freez(page);
599 #else
574 - data_size = page->max_elements * ar->config.element_size;
575 - structures_size = page->size - data_size;
576 -
600 if(page->mapped) {
578 - __atomic_sub_fetch(&ar->stats->mmap.allocations, 1, __ATOMIC_RELAXED);
579 - __atomic_sub_fetch(&ar->stats->mmap.allocated_bytes, data_size, __ATOMIC_RELAXED);
580 -
601 + stats = &ar->stats->mmap;
602 netdata_munmap(page, page->size);
603 }
604 else {
584 - __atomic_sub_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
585 - __atomic_sub_fetch(&ar->stats->malloc.allocated_bytes, data_size, __ATOMIC_RELAXED);
586 -
605 + stats = &ar->stats->malloc;
606 freez(page);
607 }
608 #endif
609 }
610
611 + size_t structures_size = sizeof(ARAL_PAGE) + max_elements * sizeof(void *);
612 + size_t data_size = max_elements * ar->config.requested_element_size;
613 + size_t padding_size = total_size - data_size - structures_size;
614 +
615 + __atomic_sub_fetch(&stats->allocations, 1, __ATOMIC_RELAXED);
616 + __atomic_sub_fetch(&stats->allocated_bytes, data_size, __ATOMIC_RELAXED);
617 + __atomic_sub_fetch(&stats->padding_bytes, padding_size, __ATOMIC_RELAXED);
618 +
619 __atomic_sub_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
620 __atomic_sub_fetch(&ar->stats->structures.allocated_bytes, structures_size, __ATOMIC_RELAXED);
621 }
@@ -766,10 +793,12 @@ void *aral_mallocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEF
793 // put the page pointer after the element
794 aral_set_page_pointer_after_element___do_NOT_have_aral_lock(ar, page, found_fr, marked);
795
769 - if(unlikely(ar->config.mmap.enabled))
770 - __atomic_add_fetch(&ar->stats->mmap.used_bytes, ar->config.element_size, __ATOMIC_RELAXED);
796 + if(unlikely(ar->config.mmap.enabled || page->mapped))
797 + __atomic_add_fetch(&ar->stats->mmap.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
798 else
772 - __atomic_add_fetch(&ar->stats->malloc.used_bytes, ar->config.element_size, __ATOMIC_RELAXED);
799 + __atomic_add_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
800 +
801 + internal_fatal((uintptr_t)found_fr % SYSTEM_REQUIRED_ALIGNMENT != 0, "Pointer is not aligned properly");
802
803 return (void *)found_fr;
804 }
@@ -827,11 +856,6 @@ void aral_freez_internal(ARAL *ar, void *ptr TRACE_ALLOCATIONS_FUNCTION_DEFINITI
856
857 if(unlikely(!ptr)) return;
858
830 - if(unlikely(ar->config.mmap.enabled))
831 - __atomic_sub_fetch(&ar->stats->mmap.used_bytes, ar->config.element_size, __ATOMIC_RELAXED);
832 - else
833 - __atomic_sub_fetch(&ar->stats->malloc.used_bytes, ar->config.element_size, __ATOMIC_RELAXED);
834 -
859 // get the page pointer
860 bool marked;
861 ARAL_PAGE *page = aral_get_page_pointer_after_element___do_NOT_have_aral_lock(ar, ptr, &marked);
@@ -839,6 +863,11 @@ void aral_freez_internal(ARAL *ar, void *ptr TRACE_ALLOCATIONS_FUNCTION_DEFINITI
863 size_t idx = mark_to_idx(marked);
864 __atomic_add_fetch(&ar->ops[idx].atomic.deallocators, 1, __ATOMIC_RELAXED);
865
866 + if(unlikely(ar->config.mmap.enabled || page->mapped))
867 + __atomic_sub_fetch(&ar->stats->mmap.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
868 + else
869 + __atomic_sub_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
870 +
871 // make this element available
872 ARAL_FREE *fr = (ARAL_FREE *)ptr;
873 fr->size = ar->config.element_size;
@@ -1093,18 +1122,22 @@ struct aral_statistics *aral_by_size_statistics(void) {
1122 return &aral_by_size_globals.shared_statistics;
1123 }
1124
1096 -size_t aral_by_size_structures(void) {
1097 - return aral_structures_from_stats(&aral_by_size_globals.shared_statistics);
1125 +size_t aral_by_size_structures_bytes(void) {
1126 + return aral_structures_bytes_from_stats(&aral_by_size_globals.shared_statistics);
1127 }
1128
1100 -size_t aral_by_size_overhead(void) {
1101 - return aral_overhead_from_stats(&aral_by_size_globals.shared_statistics);
1129 +size_t aral_by_size_free_bytes(void) {
1130 + return aral_free_bytes_from_stats(&aral_by_size_globals.shared_statistics);
1131 }
1132
1133 size_t aral_by_size_used_bytes(void) {
1134 return aral_used_bytes_from_stats(&aral_by_size_globals.shared_statistics);
1135 }
1136
1137 +size_t aral_by_size_padding_bytes(void) {
1138 + return aral_padding_bytes_from_stats(&aral_by_size_globals.shared_statistics);
1139 +}
1140 +
1141 ARAL *aral_by_size_acquire(size_t size) {
1142 spinlock_lock(&aral_by_size_globals.spinlock);
1143
src/libnetdata/aral/aral.h
+71 -24
@@ -8,53 +8,96 @@
8
9 typedef struct aral ARAL;
10
11 +struct aral_page_type_stats {
12 + CACHE_LINE_PADDING();
13 + size_t allocations;
14 + CACHE_LINE_PADDING();
15 + size_t allocated_bytes;
16 + CACHE_LINE_PADDING();
17 + size_t used_bytes;
18 + CACHE_LINE_PADDING();
19 + size_t padding_bytes;
20 +};
21 +
22 struct aral_statistics {
23 struct {
13 - alignas(64) size_t allocations;
14 - alignas(64) size_t allocated_bytes;
24 + CACHE_LINE_PADDING();
25 + size_t allocations;
26 + CACHE_LINE_PADDING();
27 + size_t allocated_bytes;
28 } structures;
29
17 - struct {
18 - alignas(64) size_t allocations;
19 - alignas(64) size_t allocated_bytes;
20 - alignas(64) size_t used_bytes;
21 - } malloc;
22 -
23 - struct {
24 - alignas(64) size_t allocations;
25 - alignas(64) size_t allocated_bytes;
26 - alignas(64) size_t used_bytes;
27 - } mmap;
30 + struct aral_page_type_stats malloc;
31 + struct aral_page_type_stats mmap;
32 };
33
34 +// --------------------------------------------------------------------------------------------------------------------
35 +
36 +const char *aral_name(ARAL *ar);
37 +
38 ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_elements, size_t max_page_size,
39 struct aral_statistics *stats, const char *filename, const char **cache_dir, bool mmap, bool lockless);
40
41 +// --------------------------------------------------------------------------------------------------------------------
42 +
43 // return the size of the element, as requested
44 size_t aral_requested_element_size(ARAL *ar);
45
46 // return the exact memory footprint of the elements
47 size_t aral_actual_element_size(ARAL *ar);
48
39 -const char *aral_name(ARAL *ar);
40 -size_t aral_overhead(ARAL *ar);
41 -size_t aral_structures(ARAL *ar);
49 +// --------------------------------------------------------------------------------------------------------------------
50 +
51 +size_t aral_optimal_malloc_page_size(void);
52 +
53 +// --------------------------------------------------------------------------------------------------------------------
54 +
55 +/*
56 + *
57 + * The total memory used by ARAL is:
58 + *
59 + * total = structures + used + free + padding
60 + *
61 + * or
62 + *
63 + * total = structures + allocated + padding
64 + *
65 + * always:
66 + *
67 + * allocated = used + free
68 + *
69 + * Hints:
70 + * - allocated, used and free are about the requested element size.
71 + * - structures includes the extension of the elements for the metadata aral needs.
72 + * - padding is lost due to alignment requirements
73 + *
74 + */
75 +
76 +size_t aral_structures_bytes(ARAL *ar);
77 +size_t aral_free_bytes(ARAL *ar);
78 +size_t aral_used_bytes(ARAL *ar);
79 +size_t aral_padding_bytes(ARAL *ar);
80 +
81 struct aral_statistics *aral_get_statistics(ARAL *ar);
43 -size_t aral_structures_from_stats(struct aral_statistics *stats);
44 -size_t aral_overhead_from_stats(struct aral_statistics *stats);
82 +
83 +size_t aral_structures_bytes_from_stats(struct aral_statistics *stats);
84 +size_t aral_free_bytes_from_stats(struct aral_statistics *stats);
85 +size_t aral_used_bytes_from_stats(struct aral_statistics *stats);
86 +size_t aral_padding_bytes_from_stats(struct aral_statistics *stats);
87 +
88 +// --------------------------------------------------------------------------------------------------------------------
89
90 ARAL *aral_by_size_acquire(size_t size);
91 void aral_by_size_release(ARAL *ar);
48 -size_t aral_by_size_structures(void);
49 -size_t aral_by_size_overhead(void);
50 -struct aral_statistics *aral_by_size_statistics(void);
92
93 +size_t aral_by_size_structures_bytes(void);
94 +size_t aral_by_size_free_bytes(void);
95 size_t aral_by_size_used_bytes(void);
53 -size_t aral_used_bytes_from_stats(struct aral_statistics *stats);
96 +size_t aral_by_size_padding_bytes(void);
97
55 -size_t aral_optimal_malloc_page_size(void);
98 +struct aral_statistics *aral_by_size_statistics(void);
99
57 -int aral_unittest(size_t elements);
100 +// --------------------------------------------------------------------------------------------------------------------
101
102 #ifdef NETDATA_TRACE_ALLOCATIONS
103
@@ -87,6 +130,10 @@ void aral_destroy_internal(ARAL *ar);
130
131 void aral_unmark_allocation(ARAL *ar, void *ptr);
132
133 +// --------------------------------------------------------------------------------------------------------------------
134 +
135 +int aral_unittest(size_t elements);
136 +
137 #endif // NETDATA_TRACE_ALLOCATIONS
138
139 #endif // ARAL_H
src/libnetdata/common.h
+6
@@ -394,6 +394,12 @@ typedef uint32_t uid_t;
394
395 // --------------------------------------------------------------------------------------------------------------------
396
397 +#define CONCAT_INDIRECT(a, b) a##b
398 +#define CONCAT(a, b) CONCAT_INDIRECT(a, b)
399 +#define CACHE_LINE_PADDING() uint8_t CONCAT(padding, __COUNTER__)[64 - sizeof(size_t)];
400 +
401 +// --------------------------------------------------------------------------------------------------------------------
402 +
403 #if defined(OS_WINDOWS)
404 #include <windows.h>
405 #include <wctype.h>
src/libnetdata/dictionary/dictionary-hashtable.h
+15
@@ -112,8 +112,13 @@ static inline size_t hashtable_destroy_judy(DICTIONARY *dict) {
112
113 pointer_destroy_index(dict);
114
115 + JudyAllocThreadPulseReset();
116 +
117 JError_t J_Error;
118 Word_t ret = JudyHSFreeArray(&dict->index.JudyHSArray, &J_Error);
119 +
120 + __atomic_add_fetch(&dict->stats->memory.index, JudyAllocThreadPulseGetAndReset(), __ATOMIC_RELAXED);
121 +
122 if(unlikely(ret == (Word_t) JERR)) {
123 netdata_log_error("DICTIONARY: Cannot destroy JudyHS, JU_ERRNO_* == %u, ID == %d",
124 JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
@@ -126,8 +131,13 @@ static inline size_t hashtable_destroy_judy(DICTIONARY *dict) {
131 }
132
133 static inline void *hashtable_insert_judy(DICTIONARY *dict, const char *name, size_t name_len) {
134 + JudyAllocThreadPulseReset();
135 +
136 JError_t J_Error;
137 Pvoid_t *Rc = JudyHSIns(&dict->index.JudyHSArray, (void *)name, name_len, &J_Error);
138 +
139 + __atomic_add_fetch(&dict->stats->memory.index, JudyAllocThreadPulseGetAndReset(), __ATOMIC_RELAXED);
140 +
141 if (unlikely(Rc == PJERR)) {
142 netdata_log_error("DICTIONARY: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d",
143 name, JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
@@ -159,8 +169,13 @@ static inline int hashtable_delete_judy(DICTIONARY *dict, const char *name, size
169 (void)item;
170 if(unlikely(!dict->index.JudyHSArray)) return 0;
171
172 + JudyAllocThreadPulseReset();
173 +
174 JError_t J_Error;
175 int ret = JudyHSDel(&dict->index.JudyHSArray, (void *)name, name_len, &J_Error);
176 +
177 + __atomic_add_fetch(&dict->stats->memory.index, JudyAllocThreadPulseGetAndReset(), __ATOMIC_RELAXED);
178 +
179 if(unlikely(ret == JERR)) {
180 netdata_log_error("DICTIONARY: Cannot delete entry with name '%s' from JudyHS, JU_ERRNO_* == %u, ID == %d",
181 name,
src/libnetdata/dictionary/dictionary-statistics.h
+2 -8
@@ -9,10 +9,7 @@
9 // memory statistics
10
11 #ifdef DICT_WITH_STATS
12 -static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) {
13 - if(key_size)
14 - __atomic_fetch_add(&dict->stats->memory.index, (long)JUDYHS_INDEX_SIZE_ESTIMATE(key_size), __ATOMIC_RELAXED);
15 -
12 +static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_size __maybe_unused, size_t item_size, size_t value_size) {
13 if(item_size)
14 __atomic_fetch_add(&dict->stats->memory.dict, (long)item_size, __ATOMIC_RELAXED);
15
@@ -20,10 +17,7 @@ static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_siz
17 __atomic_fetch_add(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
18 }
19
23 -static inline void DICTIONARY_STATS_MINUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) {
24 - if(key_size)
25 - __atomic_fetch_sub(&dict->stats->memory.index, (long)JUDYHS_INDEX_SIZE_ESTIMATE(key_size), __ATOMIC_RELAXED);
26 -
20 +static inline void DICTIONARY_STATS_MINUS_MEMORY(DICTIONARY *dict, size_t key_size __maybe_unused, size_t item_size, size_t value_size) {
21 if(item_size)
22 __atomic_fetch_sub(&dict->stats->memory.dict, (long)item_size, __ATOMIC_RELAXED);
23
src/libnetdata/dictionary/dictionary.h
+26
@@ -66,48 +66,74 @@ struct dictionary_stats {
66 const char *name; // the name of the category
67
68 struct {
69 + CACHE_LINE_PADDING();
70 size_t active; // the number of active dictionaries
71 + CACHE_LINE_PADDING();
72 size_t deleted; // the number of dictionaries queued for destruction
73 } dictionaries;
74
75 struct {
76 + CACHE_LINE_PADDING();
77 long entries; // active items in the dictionary
78 + CACHE_LINE_PADDING();
79 long pending_deletion; // pending deletion items in the dictionary
80 + CACHE_LINE_PADDING();
81 long referenced; // referenced items in the dictionary
82 } items;
83
84 struct {
85 + CACHE_LINE_PADDING();
86 size_t creations; // dictionary creations
87 + CACHE_LINE_PADDING();
88 size_t destructions; // dictionary destructions
89 + CACHE_LINE_PADDING();
90 size_t flushes; // dictionary flushes
91 + CACHE_LINE_PADDING();
92 size_t traversals; // dictionary foreach
93 + CACHE_LINE_PADDING();
94 size_t walkthroughs; // dictionary walkthrough
95 + CACHE_LINE_PADDING();
96 size_t garbage_collections; // dictionary garbage collections
97 + CACHE_LINE_PADDING();
98 size_t searches; // item searches
99 + CACHE_LINE_PADDING();
100 size_t inserts; // item inserts
101 + CACHE_LINE_PADDING();
102 size_t resets; // item resets
103 + CACHE_LINE_PADDING();
104 size_t deletes; // item deletes
105 } ops;
106
107 struct {
108 + CACHE_LINE_PADDING();
109 size_t inserts; // number of times the insert callback is called
110 + CACHE_LINE_PADDING();
111 size_t conflicts; // number of times the conflict callback is called
112 + CACHE_LINE_PADDING();
113 size_t reacts; // number of times the react callback is called
114 + CACHE_LINE_PADDING();
115 size_t deletes; // number of times the delete callback is called
116 } callbacks;
117
118 // memory
119 struct {
120 + CACHE_LINE_PADDING();
121 ssize_t index; // bytes of keys indexed (indication of the index size)
122 + CACHE_LINE_PADDING();
123 ssize_t values; // bytes of caller structures
124 + CACHE_LINE_PADDING();
125 ssize_t dict; // bytes of the structures dictionary needs
126 } memory;
127
128 // spin locks
129 struct {
130 + CACHE_LINE_PADDING();
131 size_t use_spins; // number of times a reference to item had to spin to acquire it or ignore it
132 + CACHE_LINE_PADDING();
133 size_t search_spins; // number of times a successful search result had to be thrown away
134 + CACHE_LINE_PADDING();
135 size_t insert_spins; // number of times an insertion to the hash table had to be repeated
136 + CACHE_LINE_PADDING();
137 size_t delete_spins; // number of times a deletion had to spin to get a decision
138 } spin_locks;
139 };
src/libnetdata/libjudy/judy-malloc.c
+5 -5
@@ -34,12 +34,12 @@ __attribute__((constructor)) void aral_judy_init(void) {
34 }
35 }
36
37 -size_t judy_aral_overhead(void) {
38 - return aral_overhead_from_stats(&judy_sizes_aral_statistics);
37 +size_t judy_aral_free_bytes(void) {
38 + return aral_free_bytes_from_stats(&judy_sizes_aral_statistics);
39 }
40
41 size_t judy_aral_structures(void) {
42 - return aral_structures_from_stats(&judy_sizes_aral_statistics);
42 + return aral_structures_bytes_from_stats(&judy_sizes_aral_statistics);
43 }
44
45 static ARAL *judy_size_aral(Word_t Words) {
@@ -51,11 +51,11 @@ static ARAL *judy_size_aral(Word_t Words) {
51
52 static __thread int64_t judy_allocated = 0;
53
54 -void JudyAllocThreadTelemetryReset(void) {
54 +void JudyAllocThreadPulseReset(void) {
55 judy_allocated = 0;
56 }
57
58 -int64_t JudyAllocThreadTelemetryGetAndReset(void) {
58 +int64_t JudyAllocThreadPulseGetAndReset(void) {
59 int64_t rc = judy_allocated;
60 judy_allocated = 0;
61 return rc;
src/libnetdata/libjudy/judy-malloc.h
+3 -3
@@ -5,10 +5,10 @@
5
6 #include "../libnetdata.h"
7
8 -size_t judy_aral_overhead(void);
8 +size_t judy_aral_free_bytes(void);
9 size_t judy_aral_structures(void);
10
11 -void JudyAllocThreadTelemetryReset(void);
12 -int64_t JudyAllocThreadTelemetryGetAndReset(void);
11 +void JudyAllocThreadPulseReset(void);
12 +int64_t JudyAllocThreadPulseGetAndReset(void);
13
14 #endif //NETDATA_JUDY_MALLOC_H
src/libnetdata/libjudy/vendored/JudyCommon/JudyPrivate.h
+1 -1
@@ -213,7 +213,7 @@ Leaf |< 3 > | 3 | 2 | 3 | 1 | 2 | 3 | 3
213 typedef int bool_t;
214 #endif
215
216 -#define FUNCTION // null; easy to find functions.
216 +#define FUNCTION __attribute__((no_sanitize("shift"))) // null; easy to find functions.
217
218 #ifndef TRUE
219 #define TRUE 1
src/libnetdata/libjudy/vendored/JudyL/JudyLCascade.c
-1
@@ -311,7 +311,6 @@ static int j__udyStageJBBtoJBB(
311 //
312 // NOTE: Caller must release the Leaf2 that was passed in.
313
314 -__attribute__((no_sanitize("shift")))
314 FUNCTION static Pjlb_t j__udyJLL2toJLB1(
315 uint16_t * Pjll, // array of 16-bit indexes.
316 #ifdef JUDYL
src/libnetdata/libjudy/vendored/JudyL/JudyLDecascade.c
-2
@@ -345,7 +345,6 @@ FUNCTION int j__udyBranchUToBranchB(
345 // allocation and free, in order to allow the caller to continue with a LeafB1
346 // if allocation fails.
347
348 -__attribute__((no_sanitize("shift")))
348 FUNCTION int j__udyLeafB1ToLeaf1(
349 Pjp_t Pjp, // points to LeafB1 to shrink.
350 Pvoid_t Pjpm) // for global accounting.
@@ -432,7 +431,6 @@ FUNCTION int j__udyLeafB1ToLeaf1(
431 // TBD: In this and all following functions, the caller should already be able
432 // to compute the Pop1 return value, so why return it?
433
435 -__attribute__((no_sanitize("shift")))
434 FUNCTION Word_t j__udyLeaf1ToLeaf2(
435 uint16_t * PLeaf2, // destination uint16_t * Index portion of leaf.
436 #ifdef JUDYL
src/libnetdata/libjudy/vendored/JudyL/JudyLDel.c
-1
@@ -147,7 +147,6 @@ extern Word_t j__udyLLeaf7ToLeafW(Pjlw_t, Pjv_t, Pjp_t, Word_t, Pvoid_t);
147
148 DBGCODE(uint8_t parentJPtype;) // parent branch JP type.
149
150 -__attribute__((no_sanitize("shift")))
150 FUNCTION static int j__udyDelWalk(
151 Pjp_t Pjp, // current JP under which to delete.
152 Word_t Index, // to delete.
src/libnetdata/libjudy/vendored/JudyL/JudyLGet.c
-2
@@ -44,8 +44,6 @@
44 // See the manual entry for details. Note support for "shortcut" entries to
45 // trees known to start with a JPM.
46
47 -__attribute__((no_sanitize("shift")))
48 -
47 #ifdef JUDY1
48
49 #ifdef JUDYGETINLINE
src/libnetdata/libjudy/vendored/JudyL/JudyLIns.c
-1
@@ -152,7 +152,6 @@ extern int j__udyLInsertBranch(Pjp_t Pjp, Word_t Index, Word_t Btype, Pjpm_t);
152 // Return -1 for error (details in JPM), 0 for Index already inserted, 1 for
153 // new Index inserted.
154
155 -__attribute__((no_sanitize("shift")))
155 FUNCTION static int j__udyInsWalk(
156 Pjp_t Pjp, // current JP to descend.
157 Word_t Index, // to insert.
src/libnetdata/libnetdata.c
+2 -1
@@ -454,7 +454,8 @@ void mallocz_release_as_much_memory_to_the_system(void) {
454 spinlock_lock(&spinlock);
455
456 #ifdef HAVE_C_MALLOPT
457 - size_t trim_threshold = aral_optimal_malloc_page_size();
457 + // the default is 128KiB
458 + size_t trim_threshold = 65ULL * 1024;
459 mallopt(M_TRIM_THRESHOLD, (int)trim_threshold);
460 #endif
461
src/libnetdata/simple_hashtable/simple_hashtable.h
+6 -2
@@ -69,9 +69,13 @@ static inline bool SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION(SIMPLE_HASHTABLE_KEY_T
69 #endif
70
71 // First layer of macro for token concatenation
72 -#define CONCAT_INTERNAL(a, b) a ## b
72 +#ifndef CONCAT_INDIRECT
73 +#define CONCAT_INDIRECT(a, b) a ## b
74 +#endif
75 // Second layer of macro, which ensures proper expansion
74 -#define CONCAT(a, b) CONCAT_INTERNAL(a, b)
76 +#ifndef CONCAT
77 +#define CONCAT(a, b) CONCAT_INDIRECT(a, b)
78 +#endif
79
80 // define names for all structures and structures
81 #define simple_hashtable_init_named CONCAT(simple_hashtable_init, SIMPLE_HASHTABLE_NAME)
src/libnetdata/string/string.c
+18 -3
@@ -32,7 +32,7 @@ static struct string_partition {
32 size_t deletes; // the number of successful deleted from the index
33
34 long int entries; // the number of entries in the index
35 - long int memory; // the memory used, without the JudyHS index
35 + long int memory; // the memory used, with JudyHS (accurate)
36
37 #ifdef NETDATA_INTERNAL_CHECKS
38 // internal statistics
@@ -196,10 +196,18 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
196
197 rw_spinlock_write_lock(&string_base[partition].spinlock);
198
199 + int64_t mem = 0;
200 +
201 STRING **ptr;
202 {
203 JError_t J_Error;
204 +
205 + JudyAllocThreadPulseReset();
206 +
207 Pvoid_t *Rc = JudyHSIns(&string_base[partition].JudyHSArray, (void *)str, length - 1, &J_Error);
208 +
209 + mem = JudyAllocThreadPulseGetAndReset();
210 +
211 if (unlikely(Rc == PJERR)) {
212 fatal(
213 "STRING: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d",
@@ -220,7 +228,7 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
228 *ptr = string;
229 string_base[partition].inserts++;
230 string_base[partition].entries++;
223 - string_base[partition].memory += (long)(mem_size + JUDYHS_INDEX_SIZE_ESTIMATE(length));
231 + string_base[partition].memory += (long)(mem_size + mem);
232 }
233 else {
234 // the item is already in the index
@@ -256,10 +264,17 @@ static inline void string_index_delete(STRING *string) {
264 #endif
265
266 bool deleted = false;
267 + int64_t mem = 0;
268
269 if (likely(string_base[partition].JudyHSArray)) {
270 JError_t J_Error;
271 +
272 + JudyAllocThreadPulseReset();
273 +
274 int ret = JudyHSDel(&string_base[partition].JudyHSArray, (void *)string->str, string->length - 1, &J_Error);
275 +
276 + mem = JudyAllocThreadPulseGetAndReset();
277 +
278 if (unlikely(ret == JERR)) {
279 netdata_log_error(
280 "STRING: Cannot delete entry with name '%s' from JudyHS, JU_ERRNO_* == %u, ID == %d",
@@ -276,7 +291,7 @@ static inline void string_index_delete(STRING *string) {
291 size_t mem_size = sizeof(STRING) + string->length;
292 string_base[partition].deletes++;
293 string_base[partition].entries--;
279 - string_base[partition].memory -= (long)(mem_size + JUDYHS_INDEX_SIZE_ESTIMATE(string->length));
294 + string_base[partition].memory -= (long)(mem_size + mem);
295 freez(string);
296 }
297
src/libnetdata/url/url.c
+1 -1
@@ -19,7 +19,7 @@ char to_hex(char code) {
19
20 /* Returns an url-encoded version of str */
21 /* IMPORTANT: be sure to free() the returned string after use */
22 -char *url_encode(char *str) {
22 +char *url_encode(const char *str) {
23 char *buf, *pbuf;
24
25 pbuf = buf = mallocz(strlen(str) * 3 + 1);
src/libnetdata/url/url.h
+1 -1
@@ -17,7 +17,7 @@ char to_hex(char code);
17
18 /* Returns a url-encoded version of str */
19 /* IMPORTANT: be sure to free() the returned string after use */
20 -char *url_encode(char *str);
20 +char *url_encode(const char *str);
21
22 /* Returns a url-decoded version of str */
23 /* IMPORTANT: be sure to free() the returned string after use */
src/ml/ml.cc
+35 -110
@@ -6,6 +6,7 @@
6
7 #include "ad_charts.h"
8 #include "database/sqlite/vendored/sqlite3.h"
9 +#include "streaming/stream-control.h"
10
11 #define WORKER_TRAIN_QUEUE_POP 0
12 #define WORKER_TRAIN_ACQUIRE_DIMENSION 1
@@ -20,13 +21,6 @@ sqlite3 *ml_db = NULL;
21 static netdata_mutex_t db_mutex = NETDATA_MUTEX_INITIALIZER;
22
23 typedef struct {
23 - // Time when the request for this response was made
24 - time_t request_time;
25 -
26 - // First/last entry of the dimension in DB when generating the request
27 - time_t first_entry_on_request;
28 - time_t last_entry_on_request;
29 -
24 // First/last entry of the dimension in DB when generating the response
25 time_t first_entry_on_response;
26 time_t last_entry_on_response;
@@ -47,14 +41,10 @@ typedef struct {
41 } ml_training_response_t;
42
43 static std::pair<enum ml_worker_result, ml_training_response_t>
50 -ml_dimension_calculated_numbers(ml_worker_t *worker, ml_dimension_t *dim, const ml_request_create_new_model_t &req)
44 +ml_dimension_calculated_numbers(ml_worker_t *worker, ml_dimension_t *dim)
45 {
46 ml_training_response_t training_response = {};
47
54 - training_response.request_time = req.request_time;
55 - training_response.first_entry_on_request = req.first_entry_on_request;
56 - training_response.last_entry_on_request = req.last_entry_on_request;
57 -
48 training_response.first_entry_on_response = rrddim_first_entry_s_of_tier(dim->rd, 0);
49 training_response.last_entry_on_response = rrddim_last_entry_s_of_tier(dim->rd, 0);
50
@@ -83,7 +73,7 @@ ml_dimension_calculated_numbers(ml_worker_t *worker, ml_dimension_t *dim, const
73
74 storage_engine_query_init(dim->rd->tiers[0].seb, dim->rd->tiers[0].smh, &handle,
75 training_response.query_after_t, training_response.query_before_t,
86 - STORAGE_PRIORITY_BEST_EFFORT);
76 + STORAGE_PRIORITY_SYNCHRONOUS);
77
78 size_t idx = 0;
79 memset(worker->training_cns, 0, sizeof(calculated_number_t) * max_n * (Cfg.lag_n + 1));
@@ -637,10 +627,18 @@ static void ml_dimension_update_models(ml_worker_t *worker, ml_dimension_t *dim)
627 }
628
629 static enum ml_worker_result
640 -ml_dimension_train_model(ml_worker_t *worker, ml_dimension_t *dim, const ml_request_create_new_model_t &req)
630 +ml_dimension_train_model(ml_worker_t *worker, ml_dimension_t *dim)
631 {
632 worker_is_busy(WORKER_TRAIN_QUERY);
643 - auto P = ml_dimension_calculated_numbers(worker, dim, req);
633 +
634 + spinlock_lock(&dim->slock);
635 + if (dim->mt == METRIC_TYPE_CONSTANT) {
636 + spinlock_unlock(&dim->slock);
637 + return ML_WORKER_RESULT_OK;
638 + }
639 + spinlock_unlock(&dim->slock);
640 +
641 + auto P = ml_dimension_calculated_numbers(worker, dim);
642 ml_worker_result worker_result = P.first;
643 ml_training_response_t training_response = P.second;
644
@@ -648,21 +646,8 @@ ml_dimension_train_model(ml_worker_t *worker, ml_dimension_t *dim, const ml_requ
646 spinlock_lock(&dim->slock);
647
648 dim->mt = METRIC_TYPE_CONSTANT;
651 -
652 - switch (dim->ts) {
653 - case TRAINING_STATUS_PENDING_WITH_MODEL:
654 - dim->ts = TRAINING_STATUS_TRAINED;
655 - break;
656 - case TRAINING_STATUS_PENDING_WITHOUT_MODEL:
657 - dim->ts = TRAINING_STATUS_UNTRAINED;
658 - break;
659 - default:
660 - break;
661 - }
662 -
649 dim->suppression_anomaly_counter = 0;
650 dim->suppression_window_counter = 0;
665 -
651 dim->last_training_time = training_response.last_entry_on_response;
652
653 spinlock_unlock(&dim->slock);
@@ -694,59 +679,8 @@ ml_dimension_train_model(ml_worker_t *worker, ml_dimension_t *dim, const ml_requ
679 return worker_result;
680 }
681
697 -static void
698 -ml_dimension_schedule_for_training(ml_dimension_t *dim, time_t curr_time)
699 -{
700 - switch (dim->mt) {
701 - case METRIC_TYPE_CONSTANT:
702 - return;
703 - default:
704 - break;
705 - }
706 -
707 - bool schedule_for_training = false;
708 -
709 - switch (dim->ts) {
710 - case TRAINING_STATUS_PENDING_WITH_MODEL:
711 - case TRAINING_STATUS_PENDING_WITHOUT_MODEL:
712 - schedule_for_training = false;
713 - break;
714 - case TRAINING_STATUS_UNTRAINED:
715 - schedule_for_training = true;
716 - dim->ts = TRAINING_STATUS_PENDING_WITHOUT_MODEL;
717 - break;
718 - case TRAINING_STATUS_SILENCED:
719 - case TRAINING_STATUS_TRAINED:
720 - if ((dim->last_training_time + (Cfg.train_every * dim->rd->rrdset->update_every)) < curr_time) {
721 - schedule_for_training = true;
722 - dim->ts = TRAINING_STATUS_PENDING_WITH_MODEL;
723 - }
724 - break;
725 - }
726 -
727 - if (schedule_for_training) {
728 - ml_request_create_new_model_t req;
729 -
730 - req.DLI = DimensionLookupInfo(
731 - &dim->rd->rrdset->rrdhost->machine_guid[0],
732 - dim->rd->rrdset->id,
733 - dim->rd->id
734 - );
735 - req.request_time = curr_time;
736 - req.first_entry_on_request = rrddim_first_entry_s(dim->rd);
737 - req.last_entry_on_request = rrddim_last_entry_s(dim->rd);
738 -
739 - ml_host_t *host = (ml_host_t *) dim->rd->rrdset->rrdhost->ml_host;
740 -
741 - ml_queue_item_t item;
742 - item.type = ML_QUEUE_ITEM_TYPE_CREATE_NEW_MODEL;
743 - item.create_new_model = req;
744 - ml_queue_push(host->queue, item);
745 - }
746 -}
747 -
682 bool
749 -ml_dimension_predict(ml_dimension_t *dim, time_t curr_time, calculated_number_t value, bool exists)
683 +ml_dimension_predict(ml_dimension_t *dim, calculated_number_t value, bool exists)
684 {
685 // Nothing to do if ML is disabled for this dimension
686 if (dim->mls != MACHINE_LEARNING_STATUS_ENABLED)
@@ -791,7 +725,7 @@ ml_dimension_predict(ml_dimension_t *dim, time_t curr_time, calculated_number_t
725 ml_features_preprocess(&features);
726
727 /*
794 - * Lock to predict and possibly schedule the dimension for training
728 + * Lock to predict
729 */
730 if (spinlock_trylock(&dim->slock) == 0)
731 return false;
@@ -800,19 +734,10 @@ ml_dimension_predict(ml_dimension_t *dim, time_t curr_time, calculated_number_t
734 if (!same_value)
735 dim->mt = METRIC_TYPE_VARIABLE;
736
803 - // Decide if the dimension needs to be scheduled for training
804 - ml_dimension_schedule_for_training(dim, curr_time);
805 -
806 - // Nothing to do if we don't have a model
807 - switch (dim->ts) {
808 - case TRAINING_STATUS_UNTRAINED:
809 - case TRAINING_STATUS_PENDING_WITHOUT_MODEL: {
810 - case TRAINING_STATUS_SILENCED:
811 - spinlock_unlock(&dim->slock);
812 - return false;
813 - }
814 - default:
815 - break;
737 + // Ignore silenced dimensions
738 + if (dim->ts == TRAINING_STATUS_SILENCED) {
739 + spinlock_unlock(&dim->slock);
740 + return false;
741 }
742
743 dim->suppression_window_counter++;
@@ -888,18 +813,9 @@ ml_chart_update_dimension(ml_chart_t *chart, ml_dimension_t *dim, bool is_anomal
813 case TRAINING_STATUS_UNTRAINED:
814 chart->mls.num_training_status_untrained++;
815 return;
891 - case TRAINING_STATUS_PENDING_WITHOUT_MODEL:
892 - chart->mls.num_training_status_pending_without_model++;
893 - return;
816 case TRAINING_STATUS_TRAINED:
817 chart->mls.num_training_status_trained++;
818
897 - chart->mls.num_anomalous_dimensions += is_anomalous;
898 - chart->mls.num_normal_dimensions += !is_anomalous;
899 - return;
900 - case TRAINING_STATUS_PENDING_WITH_MODEL:
901 - chart->mls.num_training_status_pending_with_model++;
902 -
819 chart->mls.num_anomalous_dimensions += is_anomalous;
820 chart->mls.num_normal_dimensions += !is_anomalous;
821 return;
@@ -997,6 +913,12 @@ ml_host_detect_once(ml_host_t *host)
913 mls_copy = host->mls;
914
915 netdata_mutex_unlock(&host->mutex);
916 +
917 + worker_is_busy(WORKER_JOB_DETECTION_DIM_CHART);
918 + ml_update_dimensions_chart(host, mls_copy);
919 +
920 + worker_is_busy(WORKER_JOB_DETECTION_HOST_CHART);
921 + ml_update_host_and_detection_rate_charts(host, host->host_anomaly_rate * 10000.0);
922 } else {
923 host->host_anomaly_rate = 0.0;
924
@@ -1009,12 +931,6 @@ ml_host_detect_once(ml_host_t *host)
931 };
932 }
933 }
1012 -
1013 - worker_is_busy(WORKER_JOB_DETECTION_DIM_CHART);
1014 - ml_update_dimensions_chart(host, mls_copy);
1015 -
1016 - worker_is_busy(WORKER_JOB_DETECTION_HOST_CHART);
1017 - ml_update_host_and_detection_rate_charts(host, host->host_anomaly_rate * 10000.0);
934 }
935
936 void *
@@ -1129,7 +1045,7 @@ static enum ml_worker_result ml_worker_create_new_model(ml_worker_t *worker, ml_
1045 }
1046
1047 ml_dimension_t *Dim = reinterpret_cast<ml_dimension_t *>(AcqDim.dimension());
1132 - return ml_dimension_train_model(worker, Dim, req);
1048 + return ml_dimension_train_model(worker, Dim);
1049 }
1050
1051 static enum ml_worker_result ml_worker_add_existing_model(ml_worker_t *worker, ml_request_add_existing_model_t req) {
@@ -1173,6 +1089,12 @@ void *ml_train_main(void *arg) {
1089 worker_register_job_name(WORKER_TRAIN_FLUSH_MODELS, "flush models");
1090
1091 while (!Cfg.training_stop) {
1092 + if(!stream_control_ml_should_be_running()) {
1093 + worker_is_idle();
1094 + stream_control_throttle();
1095 + continue;
1096 + }
1097 +
1098 worker_is_busy(WORKER_TRAIN_QUEUE_POP);
1099
1100 ml_queue_stats_t loop_stats{};
@@ -1195,6 +1117,9 @@ void *ml_train_main(void *arg) {
1117 switch (item.type) {
1118 case ML_QUEUE_ITEM_TYPE_CREATE_NEW_MODEL: {
1119 worker_res = ml_worker_create_new_model(worker, item.create_new_model);
1120 + if (worker_res != ML_WORKER_RESULT_NULL_ACQUIRED_DIMENSION) {
1121 + ml_queue_push(worker->queue, item);
1122 + }
1123 break;
1124 }
1125 case ML_QUEUE_ITEM_TYPE_ADD_EXISTING_MODEL: {
src/ml/ml_config.cc
+1 -1
@@ -46,7 +46,7 @@ void ml_config_load(ml_config_t *cfg) {
46 time_t anomaly_detection_query_duration = config_get_duration_seconds(config_section_ml, "anomaly detection grouping duration", 5 * 60);
47
48 size_t num_worker_threads = config_get_number(config_section_ml, "num training threads", os_get_system_cpus() / 4);
49 - size_t flush_models_batch_size = config_get_number(config_section_ml, "flush models batch size", 128);
49 + size_t flush_models_batch_size = config_get_number(config_section_ml, "flush models batch size", 256);
50
51 size_t suppression_window =
52 config_get_duration_seconds(config_section_ml, "dimension anomaly rate suppression window", 900);
src/ml/ml_dimension.h
+1 -1
@@ -29,7 +29,7 @@ struct ml_dimension_t {
29 };
30
31 bool
32 -ml_dimension_predict(ml_dimension_t *dim, time_t curr_time, calculated_number_t value, bool exists);
32 +ml_dimension_predict(ml_dimension_t *dim, calculated_number_t value, bool exists);
33
34 bool ml_dimension_deserialize_kmeans(const char *json_str);
35
src/ml/ml_enums.cc
-4
@@ -32,10 +32,6 @@ const char *
32 ml_training_status_to_string(enum ml_training_status ts)
33 {
34 switch (ts) {
35 - case TRAINING_STATUS_PENDING_WITH_MODEL:
36 - return "pending-with-model";
37 - case TRAINING_STATUS_PENDING_WITHOUT_MODEL:
38 - return "pending-without-model";
35 case TRAINING_STATUS_TRAINED:
36 return "trained";
37 case TRAINING_STATUS_UNTRAINED:
src/ml/ml_enums.h
-6
@@ -27,12 +27,6 @@ enum ml_training_status {
27 // We don't have a model for this dimension
28 TRAINING_STATUS_UNTRAINED,
29
30 - // Request for training sent, but we don't have any models yet
31 - TRAINING_STATUS_PENDING_WITHOUT_MODEL,
32 -
33 - // Request to update existing models sent
34 - TRAINING_STATUS_PENDING_WITH_MODEL,
35 -
30 // Have a valid, up-to-date model
31 TRAINING_STATUS_TRAINED,
32
src/ml/ml_public.cc
+26 -6
@@ -48,7 +48,7 @@ void ml_host_new(RRDHOST *rh)
48 netdata_mutex_init(&host->mutex);
49 spinlock_init(&host->type_anomaly_rate_spinlock);
50
51 - host->ml_running = true;
51 + host->ml_running = false;
52 rh->ml_host = (rrd_ml_host_t *) host;
53 }
54
@@ -104,13 +104,12 @@ void ml_host_stop(RRDHOST *rh) {
104
105 spinlock_lock(&dim->slock);
106
107 - // reset dim
108 - // TODO: should we drop in-mem models, or mark them as stale? Is it
109 - // okay to resume training straight away?
110 -
107 dim->mt = METRIC_TYPE_CONSTANT;
108 dim->ts = TRAINING_STATUS_UNTRAINED;
109 +
110 + // TODO: Check if we can remove this field.
111 dim->last_training_time = 0;
112 +
113 dim->suppression_anomaly_counter = 0;
114 dim->suppression_window_counter = 0;
115 dim->cns.clear();
@@ -290,6 +289,25 @@ void ml_dimension_new(RRDDIM *rd)
289 rd->ml_dimension = (rrd_ml_dimension_t *) dim;
290
291 metaqueue_ml_load_models(rd);
292 +
293 + // add to worker queue
294 + {
295 + RRDHOST *rh = rd->rrdset->rrdhost;
296 + ml_host_t *host = (ml_host_t *) rh->ml_host;
297 +
298 + ml_queue_item_t item;
299 + item.type = ML_QUEUE_ITEM_TYPE_CREATE_NEW_MODEL;
300 +
301 + ml_request_create_new_model_t req;
302 + req.DLI = DimensionLookupInfo(
303 + &rh->machine_guid[0],
304 + rd->rrdset->id,
305 + rd->id
306 + );
307 + item.create_new_model = req;
308 +
309 + ml_queue_push(host->queue, item);
310 + }
311 }
312
313 void ml_dimension_delete(RRDDIM *rd)
@@ -318,6 +336,8 @@ void ml_dimension_received_anomaly(RRDDIM *rd, bool is_anomalous) {
336
337 bool ml_dimension_is_anomalous(RRDDIM *rd, time_t curr_time, double value, bool exists)
338 {
339 + UNUSED(curr_time);
340 +
341 ml_dimension_t *dim = (ml_dimension_t *) rd->ml_dimension;
342 if (!dim)
343 return false;
@@ -328,7 +348,7 @@ bool ml_dimension_is_anomalous(RRDDIM *rd, time_t curr_time, double value, bool
348
349 ml_chart_t *chart = (ml_chart_t *) rd->rrdset->ml_chart;
350
331 - bool is_anomalous = ml_dimension_predict(dim, curr_time, value, exists);
351 + bool is_anomalous = ml_dimension_predict(dim, value, exists);
352 ml_chart_update_dimension(chart, dim, is_anomalous);
353
354 return is_anomalous;
src/ml/ml_queue.h
-8
@@ -10,14 +10,6 @@
10
11 typedef struct ml_request_create_new_model {
12 DimensionLookupInfo DLI;
13 -
14 - // Creation time of request
15 - time_t request_time;
16 -
17 - // First/last entry of this dimension in DB
18 - // at the point the request was made
19 - time_t first_entry_on_request;
20 - time_t last_entry_on_request;
13 } ml_request_create_new_model_t;
14
15 typedef struct ml_request_add_existing_model {
src/plugins.d/pluginsd_parser.c
+2
@@ -202,6 +202,8 @@ static inline PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, si
202 false);
203
204 rrdhost_option_set(host, RRDHOST_OPTION_VIRTUAL_HOST);
205 + rrdhost_flag_set(host, RRDHOST_FLAG_COLLECTOR_ONLINE);
206 + ml_host_start(host);
207 dyncfg_host_init(host);
208
209 if(host->rrdlabels) {
src/streaming/protocol/command-begin-set-end.c
+1 -1
@@ -30,7 +30,7 @@ stream_send_rrdset_metrics_v1_internal(BUFFER *wb, RRDSET *st, struct sender_sta
30 buffer_fast_strcat(wb, "\n", 1);
31 }
32 else {
33 - internal_error(true, "STREAM: 'host:%s/chart:%s/dim:%s' flag 'exposed' is updated but not exposed",
33 + internal_error(true, "STREAM SEND '%s': 'chart:%s/dim:%s' flag 'exposed' is updated but not exposed",
34 rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_id(rd));
35 // we will include it in the next iteration
36 rrddim_metadata_updated(rd);
src/streaming/protocol/command-nodeid.c
+6 -6
@@ -51,7 +51,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
51 ND_UUID claim_id;
52 if (uuid_parse(claim_id_str ? claim_id_str : "", claim_id.uuid) != 0) {
53 nd_log(NDLS_DAEMON, NDLP_ERR,
54 - "STREAM %s [send to %s] received invalid claim id '%s'",
54 + "STREAM SEND '%s' [to %s] received invalid claim id '%s'",
55 rrdhost_hostname(s->host), s->connected_to,
56 claim_id_str ? claim_id_str : "(unset)");
57 return;
@@ -60,7 +60,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
60 ND_UUID node_id;
61 if(uuid_parse(node_id_str ? node_id_str : "", node_id.uuid) != 0) {
62 nd_log(NDLS_DAEMON, NDLP_ERR,
63 - "STREAM %s [send to %s] received an invalid node id '%s'",
63 + "STREAM SEND '%s' [to %s] received an invalid node id '%s'",
64 rrdhost_hostname(s->host), s->connected_to,
65 node_id_str ? node_id_str : "(unset)");
66 return;
@@ -68,14 +68,14 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
68
69 if (!UUIDiszero(s->host->aclk.claim_id_of_parent) && !UUIDeq(s->host->aclk.claim_id_of_parent, claim_id))
70 nd_log(NDLS_DAEMON, NDLP_INFO,
71 - "STREAM %s [send to %s] changed parent's claim id to %s",
71 + "STREAM SEND '%s' [to %s] changed parent's claim id to %s",
72 rrdhost_hostname(s->host), s->connected_to,
73 claim_id_str ? claim_id_str : "(unset)");
74
75 if(!UUIDiszero(s->host->node_id) && !UUIDeq(s->host->node_id, node_id)) {
76 if(claimed) {
77 nd_log(NDLS_DAEMON, NDLP_WARNING,
78 - "STREAM %s [send to %s] parent reports different node id '%s', but we are claimed. Ignoring it.",
78 + "STREAM SEND '%s' [to %s] parent reports different node id '%s', but we are claimed. Ignoring it.",
79 rrdhost_hostname(s->host), s->connected_to,
80 node_id_str ? node_id_str : "(unset)");
81 return;
@@ -83,7 +83,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
83 else {
84 update_node_id = true;
85 nd_log(NDLS_DAEMON, NDLP_WARNING,
86 - "STREAM %s [send to %s] changed node id to %s",
86 + "STREAM SEND '%s' [to %s] changed node id to %s",
87 rrdhost_hostname(s->host), s->connected_to,
88 node_id_str ? node_id_str : "(unset)");
89 }
@@ -91,7 +91,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
91
92 if(!url || !*url) {
93 nd_log(NDLS_DAEMON, NDLP_ERR,
94 - "STREAM %s [send to %s] received an invalid cloud URL '%s'",
94 + "STREAM SEND '%s' [to %s] received an invalid cloud URL '%s'",
95 rrdhost_hostname(s->host), s->connected_to,
96 url ? url : "(unset)");
97 return;
src/streaming/protocol/commands.c
+9 -3
@@ -19,13 +19,19 @@ RRDSET_STREAM_BUFFER stream_send_metrics_init(RRDSET *st, time_t wall_clock_time
19 // check if we are not connected
20 if(unlikely(!(host_flags & RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS))) {
21
22 - if(unlikely(!(host_flags & (RRDHOST_FLAG_STREAM_SENDER_ADDED | RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED))))
22 + if(unlikely((host_flags & RRDHOST_FLAG_COLLECTOR_ONLINE) &&
23 + !(host_flags & RRDHOST_FLAG_STREAM_SENDER_ADDED)))
24 stream_sender_start_host(host);
25
26 if(unlikely(!(host_flags & RRDHOST_FLAG_STREAM_SENDER_LOGGED_STATUS))) {
27 rrdhost_flag_set(host, RRDHOST_FLAG_STREAM_SENDER_LOGGED_STATUS);
28 +
29 + // this message is logged in 2 cases:
30 + // - the parent is connected, but not yet available for streaming data
31 + // - the parent just disconnected, so local data are not streamed to parent
32 +
33 nd_log(NDLS_DAEMON, NDLP_INFO,
28 - "STREAM SEND %s: connected but streaming is not ready yet...",
34 + "STREAM SEND '%s': streaming is not ready, not sending data to a parent...",
35 rrdhost_hostname(host));
36 }
37
@@ -33,7 +39,7 @@ RRDSET_STREAM_BUFFER stream_send_metrics_init(RRDSET *st, time_t wall_clock_time
39 }
40 else if(unlikely(host_flags & RRDHOST_FLAG_STREAM_SENDER_LOGGED_STATUS)) {
41 nd_log(NDLS_DAEMON, NDLP_INFO,
36 - "STREAM SEND %s: streaming is ready, sending metrics to parent...",
42 + "STREAM SEND '%s': streaming is ready, sending metrics to parent...",
43 rrdhost_hostname(host));
44 rrdhost_flag_clear(host, RRDHOST_FLAG_STREAM_SENDER_LOGGED_STATUS);
45 }
src/streaming/replication.c
+43 -26
@@ -50,7 +50,7 @@ struct replication_query_statistics replication_get_query_statistics(void) {
50 return ret;
51 }
52
53 -size_t replication_buffers_allocated = 0;
53 +static size_t replication_buffers_allocated = 0;
54
55 size_t replication_allocated_buffers(void) {
56 return __atomic_load_n(&replication_buffers_allocated, __ATOMIC_RELAXED);
@@ -155,7 +155,7 @@ static struct replication_query *replication_query_prepare(
155 if (st->last_updated.tv_sec > q->query.before) {
156 #ifdef NETDATA_LOG_REPLICATION_REQUESTS
157 internal_error(true,
158 - "STREAM_SENDER REPLAY: 'host:%s/chart:%s' "
158 + "STREAM SEND REPLAY: 'host:%s/chart:%s' "
159 "has start_streaming = true, "
160 "adjusting replication before timestamp from %llu to %llu",
161 rrdhost_hostname(st->rrdhost), rrdset_id(st),
@@ -178,7 +178,7 @@ static struct replication_query *replication_query_prepare(
178
179 if (unlikely(rd_dfe.counter >= q->dimensions)) {
180 internal_error(true,
181 - "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s' has more dimensions than the replicated ones",
181 + "STREAM SEND REPLAY ERROR: 'host:%s/chart:%s' has more dimensions than the replicated ones",
182 rrdhost_hostname(st->rrdhost), rrdset_id(st));
183 break;
184 }
@@ -192,6 +192,7 @@ static struct replication_query *replication_query_prepare(
192 STORAGE_PRIORITY priority = q->query.locked_data_collection ? STORAGE_PRIORITY_HIGH : STORAGE_PRIORITY_LOW;
193 if(synchronous) priority = STORAGE_PRIORITY_SYNCHRONOUS;
194
195 + stream_control_replication_query_started();
196 storage_engine_query_init(q->backend, rd->tiers[0].smh, &d->handle,
197 q->query.after, q->query.before, priority);
198 d->enabled = true;
@@ -276,6 +277,7 @@ static void replication_query_finalize(BUFFER *wb, struct replication_query *q,
277 if (unlikely(!d->enabled)) continue;
278
279 storage_engine_query_finalize(&d->handle);
280 + stream_control_replication_query_finished();
281
282 dictionary_acquired_item_release(d->dict, d->rda);
283
@@ -362,7 +364,7 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
364
365 nd_log_limit_static_global_var(erl, 1, 0);
366 nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
365 - "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s/dim:%s': db does not advance the query "
367 + "STREAM SEND REPLAY: 'host:%s/chart:%s/dim:%s': db does not advance the query "
368 "beyond time %llu (tried 1000 times to get the next point and always got back a point in the past)",
369 rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st), rrddim_id(d->rd),
370 (unsigned long long) now);
@@ -412,8 +414,7 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
414 #ifdef NETDATA_INTERNAL_CHECKS
415 nd_log_limit_static_global_var(erl, 1, 0);
416 nd_log_limit(&erl, NDLS_DAEMON, NDLP_WARNING,
415 - "REPLAY WARNING: 'host:%s/chart:%s' "
416 - "misaligned dimensions, "
417 + "STREAM SEND REPLAY WARNING: 'host:%s/chart:%s' misaligned dimensions, "
418 "update every (min: %ld, max: %ld), "
419 "start time (min: %ld, max: %ld), "
420 "end time (min %ld, max %ld), "
@@ -448,9 +449,10 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
449 q->query.before = last_end_time_in_buffer;
450 q->query.enable_streaming = false;
451
451 - internal_error(true, "REPLICATION: current buffer size %zu is more than the "
452 - "max message size %zu for chart '%s' of host '%s'. "
453 - "Interrupting replication request (%ld to %ld, %s) at %ld to %ld, %s.",
452 + internal_error(true,
453 + "STREAM SEND REPLAY: current buffer size %zu is more than the "
454 + "max message size %zu for chart '%s' of host '%s'. "
455 + "Interrupting replication request (%ld to %ld, %s) at %ld to %ld, %s.",
456 buffer_strlen(wb), max_msg_size, rrdset_id(q->st), rrdhost_hostname(q->st->rrdhost),
457 q->request.after, q->request.before, q->request.enable_streaming?"true":"false",
458 q->query.after, q->query.before, q->query.enable_streaming?"true":"false");
@@ -528,14 +530,14 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
530 log_date(actual_after_buf, LOG_DATE_LENGTH, actual_after);
531 log_date(actual_before_buf, LOG_DATE_LENGTH, actual_before);
532 internal_error(true,
531 - "STREAM_SENDER REPLAY: 'host:%s/chart:%s': sending data %llu [%s] to %llu [%s] (requested %llu [delta %lld] to %llu [delta %lld])",
533 + "STREAM SEND REPLAY: 'host:%s/chart:%s': sending data %llu [%s] to %llu [%s] (requested %llu [delta %lld] to %llu [delta %lld])",
534 rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st),
535 (unsigned long long)actual_after, actual_after_buf, (unsigned long long)actual_before, actual_before_buf,
536 (unsigned long long)after, (long long)(actual_after - after), (unsigned long long)before, (long long)(actual_before - before));
537 }
538 else
539 internal_error(true,
538 - "STREAM_SENDER REPLAY: 'host:%s/chart:%s': nothing to send (requested %llu to %llu)",
540 + "STREAM SEND REPLAY: 'host:%s/chart:%s': nothing to send (requested %llu to %llu)",
541 rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st),
542 (unsigned long long)after, (unsigned long long)before);
543 #endif // NETDATA_LOG_REPLICATION_REQUESTS
@@ -706,12 +708,14 @@ bool replication_response_execute_and_finalize(struct replication_query *q, size
708 st->stream.snd.resync_time_s = 0;
709
710 #ifdef NETDATA_LOG_REPLICATION_REQUESTS
709 - internal_error(true, "STREAM_SENDER REPLAY: 'host:%s/chart:%s' streaming starts",
711 + internal_error(true, "STREAM SEND REPLAY: 'host:%s/chart:%s' streaming starts",
712 rrdhost_hostname(st->rrdhost), rrdset_id(st));
713 #endif
714 }
715 else
714 - internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' received start streaming command, but the chart is not in progress replicating",
716 + internal_error(true,
717 + "STREAM SEND REPLAY ERROR: 'host:%s/chart:%s' "
718 + "received start streaming command, but the chart is not in progress replicating",
719 rrdhost_hostname(st->rrdhost), rrdset_id(st));
720 }
721 }
@@ -771,7 +775,7 @@ static void replicate_log_request(struct replication_request_details *r, const c
775 nd_log_limit_static_global_var(erl, 1, 0);
776 nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
777 #endif
774 - "REPLAY ERROR: 'host:%s/chart:%s' child sent: "
778 + "STREAM SEND REPLAY ERROR: 'host:%s/chart:%s' child sent: "
779 "db from %ld to %ld%s, wall clock time %ld, "
780 "last request from %ld to %ld, "
781 "issue: %s - "
@@ -809,7 +813,7 @@ static bool send_replay_chart_cmd(struct replication_request_details *r, const c
813 log_date(wanted_before_buf, LOG_DATE_LENGTH, r->wanted.before);
814
815 internal_error(true,
812 - "REPLAY: 'host:%s/chart:%s' sending replication request %ld [%s] to %ld [%s], start streaming '%s': %s: "
816 + "STREAM SEND REPLAY: 'host:%s/chart:%s' sending replication request %ld [%s] to %ld [%s], start streaming '%s': %s: "
817 "last[%ld - %ld] child[%ld - %ld, now %ld %s] local[%ld - %ld, now %ld] gap[%ld - %ld %s] %s"
818 , rrdhost_hostname(r->host), rrdset_id(r->st)
819 , r->wanted.after, wanted_after_buf
@@ -838,7 +842,7 @@ static bool send_replay_chart_cmd(struct replication_request_details *r, const c
842
843 ssize_t ret = r->caller.callback(buffer, r->caller.parser, STREAM_TRAFFIC_TYPE_REPLICATION);
844 if (ret < 0) {
841 - netdata_log_error("REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %zd)",
845 + netdata_log_error("STREAM SEND REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %zd)",
846 rrdhost_hostname(r->host), rrdset_id(r->st), ret);
847 return false;
848 }
@@ -1277,7 +1281,7 @@ static void replication_sort_entry_del(struct replication_request *rq, bool buff
1281 }
1282
1283 if (!rse_to_delete)
1280 - fatal("REPLAY: 'host:%s/chart:%s' Cannot find sort entry to delete for time %ld.",
1284 + fatal("STREAM SEND REPLAY: 'host:%s/chart:%s' Cannot find sort entry to delete for time %ld.",
1285 rrdhost_hostname(rq->sender->host), string2str(rq->chart_id), rq->after);
1286
1287 }
@@ -1380,7 +1384,7 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1384 // we can replace this command
1385 internal_error(
1386 true,
1383 - "STREAM %s [send to %s]: REPLAY: 'host:%s/chart:%s' replacing duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1387 + "STREAM SEND '%s' [to %s]: REPLAY: 'host:%s/chart:%s' replacing duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1388 rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host), dictionary_acquired_item_name(item),
1389 (unsigned long long)rq->after, (unsigned long long)rq->before, rq->start_streaming ? "true" : "false",
1390 (unsigned long long)rq_new->after, (unsigned long long)rq_new->before, rq_new->start_streaming ? "true" : "false");
@@ -1393,7 +1397,7 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1397 replication_sort_entry_add(rq);
1398 internal_error(
1399 true,
1396 - "STREAM %s [send to %s]: REPLAY: 'host:%s/chart:%s' adding duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1400 + "STREAM SEND '%s' [to %s]: REPLAY: 'host:%s/chart:%s' adding duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1401 rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host), dictionary_acquired_item_name(item),
1402 (unsigned long long)rq->after, (unsigned long long)rq->before, rq->start_streaming ? "true" : "false",
1403 (unsigned long long)rq_new->after, (unsigned long long)rq_new->before, rq_new->start_streaming ? "true" : "false");
@@ -1401,7 +1405,7 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1405 else {
1406 internal_error(
1407 true,
1404 - "STREAM %s [send to %s]: REPLAY: 'host:%s/chart:%s' ignoring duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1408 + "STREAM SEND '%s' [to %s]: REPLAY: 'host:%s/chart:%s' ignoring duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1409 rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host),
1410 dictionary_acquired_item_name(item),
1411 (unsigned long long) rq->after, (unsigned long long) rq->before, rq->start_streaming ? "true" : "false",
@@ -1445,7 +1449,7 @@ static bool replication_execute_request(struct replication_request *rq, bool wor
1449 }
1450
1451 if(!rq->st) {
1448 - internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' not found",
1452 + internal_error(true, "STREAM SEND REPLAY ERROR: 'host:%s/chart:%s' not found",
1453 rrdhost_hostname(rq->sender->host), string2str(rq->chart_id));
1454
1455 goto cleanup;
@@ -1573,7 +1577,8 @@ static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1577 host->sender &&
1578 !stream_sender_pending_replication_requests(host->sender) &&
1579 dictionary_entries(host->sender->replication.requests) != 0,
1576 - "REPLICATION SUMMARY: 'host:%s' reports %zu pending replication requests, but its chart replication index says there are %zu charts pending replication",
1580 + "STREAM SEND REPLAY SUMMARY: 'host:%s' reports %zu pending replication requests, "
1581 + "but its chart replication index says there are %zu charts pending replication",
1582 rrdhost_hostname(host),
1583 stream_sender_pending_replication_requests(host->sender),
1584 dictionary_entries(host->sender->replication.requests)
@@ -1591,7 +1596,7 @@ static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1596 if(!flags) {
1597 internal_error(
1598 true,
1594 - "REPLICATION SUMMARY: 'host:%s/chart:%s' is neither IN PROGRESS nor FINISHED",
1599 + "STREAM SEND REPLAY SUMMARY: 'host:%s/chart:%s' is neither IN PROGRESS nor FINISHED",
1600 rrdhost_hostname(host), rrdset_id(st)
1601 );
1602 is_error = true;
@@ -1600,7 +1605,7 @@ static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1605 if(!(flags & RRDSET_FLAG_SENDER_REPLICATION_FINISHED) || (flags & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS)) {
1606 internal_error(
1607 true,
1603 - "REPLICATION SUMMARY: 'host:%s/chart:%s' is IN PROGRESS although replication is finished",
1608 + "STREAM SEND REPLAY SUMMARY: 'host:%s/chart:%s' is IN PROGRESS although replication is finished",
1609 rrdhost_hostname(host), rrdset_id(st)
1610 );
1611 is_error = true;
@@ -1614,7 +1619,7 @@ static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1619 rrdset_foreach_done(st);
1620
1621 internal_error(errors,
1617 - "REPLICATION SUMMARY: 'host:%s' finished replicating %zu charts, but %zu charts are still in progress although replication finished",
1622 + "STREAM SEND REPLAY SUMMARY: 'host:%s' finished replicating %zu charts, but %zu charts are still in progress although replication finished",
1623 rrdhost_hostname(host), ok, errors);
1624
1625 return errors;
@@ -1830,6 +1835,12 @@ static void *replication_worker_thread(void *ptr __maybe_unused) {
1835 replication_initialize_workers(false);
1836
1837 while (service_running(SERVICE_REPLICATION)) {
1838 + if(!stream_control_replication_should_be_running()) {
1839 + worker_is_idle();
1840 + stream_control_throttle();
1841 + continue;
1842 + }
1843 +
1844 if (unlikely(replication_pipeline_execute_next() == REQUEST_QUEUE_EMPTY)) {
1845 sender_commit_thread_buffer_free();
1846 worker_is_busy(WORKER_JOB_WAIT);
@@ -1880,7 +1891,7 @@ void *replication_thread_main(void *ptr) {
1891
1892 int nodes = (int)dictionary_entries(rrdhost_root_index);
1893 int cpus = (int)get_netdata_cpus();
1883 - int threads = MIN(cpus * 1 / 3, nodes / 10);
1894 + int threads = cpus / 2;
1895 if (threads < 1) threads = 1;
1896 else if (threads > MAX_REPLICATION_THREADS) threads = MAX_REPLICATION_THREADS;
1897
@@ -1926,6 +1937,12 @@ void *replication_thread_main(void *ptr) {
1937
1938 while(service_running(SERVICE_REPLICATION)) {
1939
1940 + if(!stream_control_replication_should_be_running()) {
1941 + worker_is_idle();
1942 + stream_control_throttle();
1943 + continue;
1944 + }
1945 +
1946 // statistics
1947 usec_t now_mono_ut = now_monotonic_usec();
1948 if(unlikely(now_mono_ut - last_now_mono_ut > default_rrd_update_every * USEC_PER_SEC)) {
src/streaming/replication.h
+8
@@ -6,6 +6,10 @@
6 #include "daemon/common.h"
7 #include "stream-circular-buffer.h"
8
9 +#ifdef __cplusplus
10 +extern "C" {
11 +#endif
12 +
13 struct parser;
14
15 struct replication_query_statistics {
@@ -36,4 +40,8 @@ void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s);
40 size_t replication_allocated_memory(void);
41 size_t replication_allocated_buffers(void);
42
43 +#ifdef __cplusplus
44 +}
45 +#endif
46 +
47 #endif /* REPLICATION_H */
src/streaming/rrdhost-status.c
+1 -1
@@ -132,7 +132,7 @@ void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) {
132 rrdhost_receiver_lock(host);
133 s->ingest.hops = (int16_t)(host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1);
134 bool has_receiver = false;
135 - if (host->receiver && !rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED)) {
135 + if (host->receiver && rrdhost_flag_check(host, RRDHOST_FLAG_COLLECTOR_ONLINE)) {
136 has_receiver = true;
137 s->ingest.replication.instances = rrdhost_receiver_replicating_charts(host);
138 s->ingest.replication.completion = host->stream.rcv.status.replication.percent;
src/streaming/stream-capabilities.c
+2 -2
@@ -80,7 +80,7 @@ void log_receiver_capabilities(struct receiver_state *rpt) {
80 BUFFER *wb = buffer_create(100, NULL);
81 stream_capabilities_to_string(wb, rpt->capabilities);
82
83 - nd_log_daemon(NDLP_INFO, "STREAM %s [receive from [%s]:%s]: established link with negotiated capabilities: %s",
83 + nd_log_daemon(NDLP_INFO, "STREAM RECEIVE '%s' [from [%s]:%s]: established link with negotiated capabilities: %s",
84 rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, buffer_tostring(wb));
85
86 buffer_free(wb);
@@ -90,7 +90,7 @@ void log_sender_capabilities(struct sender_state *s) {
90 BUFFER *wb = buffer_create(100, NULL);
91 stream_capabilities_to_string(wb, s->capabilities);
92
93 - nd_log_daemon(NDLP_INFO, "STREAM %s [send to %s]: established link with negotiated capabilities: %s",
93 + nd_log_daemon(NDLP_INFO, "STREAM SEND '%s' [to %s]: established link with negotiated capabilities: %s",
94 rrdhost_hostname(s->host), s->connected_to, buffer_tostring(wb));
95
96 buffer_free(wb);
src/streaming/stream-circular-buffer.c
+17 -12
@@ -3,8 +3,6 @@
3 #include "stream.h"
4 #include "stream-sender-internals.h"
5
6 -#define STREAM_CIRCULAR_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3
7 -
6 struct stream_circular_buffer {
7 struct circular_buffer *cb;
8 STREAM_CIRCULAR_BUFFER_STATS stats;
@@ -41,10 +39,9 @@ STREAM_CIRCULAR_BUFFER *stream_circular_buffer_create(void) {
39 }
40
41 // returns true if it increased the buffer size
44 -bool stream_circular_buffer_set_max_size_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t uncompressed_msg_size, bool force) {
45 - size_t wanted = uncompressed_msg_size * STREAM_CIRCULAR_BUFFER_ADAPT_TO_TIMES_MAX_SIZE;
46 - if(force || scb->cb->max_size < wanted) {
47 - scb->cb->max_size = wanted;
42 +bool stream_circular_buffer_set_max_size_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t max_size, bool force) {
43 + if(force || scb->cb->max_size < max_size) {
44 + scb->cb->max_size = max_size;
45 scb->stats.bytes_max_size = scb->cb->max_size;
46 __atomic_store_n(&scb->atomic.max_size, scb->cb->max_size, __ATOMIC_RELAXED);
47 stream_circular_buffer_stats_update_unsafe(scb);
@@ -81,8 +78,9 @@ void stream_circular_buffer_recreate_timed_unsafe(STREAM_CIRCULAR_BUFFER *scb, u
78 scb->stats.recreates++; // we increase even if we don't do it, to have sender_start() recreate its buffers
79
80 if(scb->cb && scb->cb->size > CBUFFER_INITIAL_SIZE) {
81 + size_t max_size = scb->cb->max_size;
82 cbuffer_free(scb->cb);
85 - scb->cb = cbuffer_new(CBUFFER_INITIAL_SIZE, stream_send.buffer_max_size, &netdata_buffers_statistics.cbuffers_streaming);
83 + scb->cb = cbuffer_new(CBUFFER_INITIAL_SIZE, max_size, &netdata_buffers_statistics.cbuffers_streaming);
84 }
85 }
86
@@ -96,15 +94,22 @@ void stream_circular_buffer_destroy(STREAM_CIRCULAR_BUFFER *scb) {
94 }
95
96 // adds data to the circular buffer, returns false when it can't (buffer is full)
99 -bool stream_circular_buffer_add_unsafe(STREAM_CIRCULAR_BUFFER *scb, const char *data, size_t bytes_actual, size_t bytes_uncompressed, STREAM_TRAFFIC_TYPE type) {
97 +bool stream_circular_buffer_add_unsafe(
98 + STREAM_CIRCULAR_BUFFER *scb, const char *data,
99 + size_t bytes_actual, size_t bytes_uncompressed, STREAM_TRAFFIC_TYPE type, bool autoscale) {
100 scb->stats.adds++;
101 scb->stats.bytes_added += bytes_actual;
102 scb->stats.bytes_uncompressed += bytes_uncompressed;
103 scb->stats.bytes_sent_by_type[type] += bytes_actual;
104 - bool rc = cbuffer_add_unsafe(scb->cb, data, bytes_actual) == 0;
105 - if(rc)
106 - stream_circular_buffer_stats_update_unsafe(scb);
107 - return rc;
104 +
105 + if(unlikely(autoscale && cbuffer_available_size_unsafe(scb->cb) < bytes_actual))
106 + stream_circular_buffer_set_max_size_unsafe(scb, scb->cb->max_size * 2, true);
107 +
108 + if(unlikely(cbuffer_add_unsafe(scb->cb, data, bytes_actual) != 0))
109 + return false;
110 +
111 + stream_circular_buffer_stats_update_unsafe(scb);
112 + return true;
113 }
114
115 // return the first available chunk at the beginning of the buffer
src/streaming/stream-circular-buffer.h
+14 -2
@@ -6,10 +6,16 @@
6 #include "libnetdata/libnetdata.h"
7 #include "stream-traffic-types.h"
8
9 +#ifdef __cplusplus
10 +extern "C" {
11 +#endif
12 +
13 #define CBUFFER_INITIAL_SIZE (16 * 1024)
14 #define CBUFFER_INITIAL_MAX_SIZE (10 * 1024 * 1024)
15 #define THREAD_BUFFER_INITIAL_SIZE (8192)
16
17 +#define STREAM_CIRCULAR_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3
18 +
19 typedef struct stream_circular_buffer_stats {
20 size_t adds;
21 size_t sends;
@@ -48,7 +54,7 @@ void stream_circular_buffer_recreate_timed_unsafe(STREAM_CIRCULAR_BUFFER *scb, u
54
55 // returns true if it increased the buffer size
56 // if it changes the size, it updates the statistics
51 -bool stream_circular_buffer_set_max_size_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t uncompressed_msg_size, bool force);
57 +bool stream_circular_buffer_set_max_size_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t max_size, bool force);
58
59 // returns a pointer to the current circular buffer statistics
60 // copy it if you plan to use it without a lock
@@ -71,7 +77,9 @@ usec_t stream_circular_buffer_get_since_ut(STREAM_CIRCULAR_BUFFER *scb);
77
78 // adds data to the end of the circular buffer, returns false when it can't (buffer is full)
79 // it updates the statistics
74 -bool stream_circular_buffer_add_unsafe(STREAM_CIRCULAR_BUFFER *scb, const char *data, size_t bytes_actual, size_t bytes_uncompressed, STREAM_TRAFFIC_TYPE type);
80 +bool stream_circular_buffer_add_unsafe(
81 + STREAM_CIRCULAR_BUFFER *scb, const char *data, size_t bytes_actual, size_t bytes_uncompressed,
82 + STREAM_TRAFFIC_TYPE type, bool autoscale);
83
84 // returns a pointer to the beginning of the buffer, and its size in bytes
85 size_t stream_circular_buffer_get_unsafe(STREAM_CIRCULAR_BUFFER *scb, char **chunk);
@@ -80,4 +88,8 @@ size_t stream_circular_buffer_get_unsafe(STREAM_CIRCULAR_BUFFER *scb, char **chu
88 // it updates the statistics
89 void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes);
90
91 +#ifdef __cplusplus
92 +}
93 +#endif
94 +
95 #endif //NETDATA_STREAM_CIRCULAR_BUFFER_H
src/streaming/stream-compression/compression.h
+4 -1
@@ -124,7 +124,10 @@ static inline size_t stream_decompress_decode_signature(const char *data, size_t
124 if (unlikely(data_size != STREAM_COMPRESSION_SIGNATURE_SIZE))
125 return 0;
126
127 - stream_compression_signature_t sign = *(stream_compression_signature_t *)data;
127 + stream_compression_signature_t sign;
128 + memcpy(&sign, data, sizeof(stream_compression_signature_t)); // Safe copy to aligned variable
129 + // stream_compression_signature_t sign = *(stream_compression_signature_t *)data;
130 +
131 if (unlikely((sign & STREAM_COMPRESSION_SIGNATURE_MASK) != STREAM_COMPRESSION_SIGNATURE))
132 return 0;
133
src/streaming/stream-conf.c
+3 -6
@@ -194,7 +194,7 @@ void stream_conf_receiver_config(struct receiver_state *rpt, struct stream_recei
194 rrd_memory_mode_name(default_rrd_memory_mode))));
195
196 if (unlikely(config->mode == RRD_MEMORY_MODE_DBENGINE && !dbengine_enabled)) {
197 - netdata_log_error("STREAM '%s' [receive from %s:%s]: "
197 + netdata_log_error("STREAM RECEIVE '%s' [from [%s]:%s]: "
198 "dbengine is not enabled, falling back to default."
199 , rpt->hostname
200 , rpt->client_ip, rpt->client_port
@@ -270,11 +270,8 @@ void stream_conf_receiver_config(struct receiver_state *rpt, struct stream_recei
270 stream_parse_compression_order(
271 config,
272 appconfig_get(
273 - &stream_config,
274 - machine_guid,
275 - "compression algorithms order",
276 - appconfig_get(
277 - &stream_config, api_key, "compression algorithms order", STREAM_COMPRESSION_ALGORITHMS_ORDER)));
273 + &stream_config, machine_guid, "compression algorithms order",
274 + appconfig_get(&stream_config, api_key, "compression algorithms order", STREAM_COMPRESSION_ALGORITHMS_ORDER)));
275 }
276
277 config->ephemeral =
src/streaming/stream-connector.c
+80 -160
@@ -2,49 +2,6 @@
2
3 #include "stream-sender-internals.h"
4
5 -typedef struct {
6 - char *os_name;
7 - char *os_id;
8 - char *os_version;
9 - char *kernel_name;
10 - char *kernel_version;
11 -} stream_encoded_t;
12 -
13 -static void rrdpush_encode_variable(stream_encoded_t *se, RRDHOST *host) {
14 - se->os_name = (host->system_info->host_os_name)?url_encode(host->system_info->host_os_name):strdupz("");
15 - se->os_id = (host->system_info->host_os_id)?url_encode(host->system_info->host_os_id):strdupz("");
16 - se->os_version = (host->system_info->host_os_version)?url_encode(host->system_info->host_os_version):strdupz("");
17 - se->kernel_name = (host->system_info->kernel_name)?url_encode(host->system_info->kernel_name):strdupz("");
18 - se->kernel_version = (host->system_info->kernel_version)?url_encode(host->system_info->kernel_version):strdupz("");
19 -}
20 -
21 -static void rrdpush_clean_encoded(stream_encoded_t *se) {
22 - if (se->os_name) {
23 - freez(se->os_name);
24 - se->os_name = NULL;
25 - }
26 -
27 - if (se->os_id) {
28 - freez(se->os_id);
29 - se->os_id = NULL;
30 - }
31 -
32 - if (se->os_version) {
33 - freez(se->os_version);
34 - se->os_version = NULL;
35 - }
36 -
37 - if (se->kernel_name) {
38 - freez(se->kernel_name);
39 - se->kernel_name = NULL;
40 - }
41 -
42 - if (se->kernel_version) {
43 - freez(se->kernel_version);
44 - se->kernel_version = NULL;
45 - }
46 -}
47 -
5 static struct {
6 const char *response;
7 const char *status;
@@ -152,7 +109,7 @@ static struct {
109 .dynamic = false,
110 .error = "remote server is initializing, we should try later",
111 .worker_job_id = WORKER_SENDER_CONNECTOR_JOB_DISCONNECT_BAD_HANDSHAKE,
155 - .postpone_reconnect_seconds = 2 * 60, // 2 minute
112 + .postpone_reconnect_seconds = 30, // 30 seconds
113 .priority = NDLP_NOTICE,
114 },
115
@@ -303,12 +260,23 @@ stream_connect_validate_first_response(RRDHOST *host, struct sender_state *s, ch
260 rfc3339_datetime_ut(buf, sizeof(buf), stream_parent_get_reconnection_ut(host->stream.snd.parents.current), 0, false);
261
262 nd_log(NDLS_DAEMON, priority,
306 - "STREAM %s [send to %s]: %s - will retry in %d secs, at %s",
263 + "STREAM CONNECT '%s' [to %s]: %s - will retry in %d secs, at %s",
264 rrdhost_hostname(host), s->connected_to, error, delay, buf);
265
266 return false;
267 }
268
269 +static inline void buffer_key_value_urlencode(BUFFER *wb, const char *key, const char *value) {
270 + char *encoded = NULL;
271 +
272 + if(value && *value)
273 + encoded = url_encode(value);
274 +
275 + buffer_sprintf(wb, "%s=%s", key, encoded ? encoded : "");
276 +
277 + freez(encoded);
278 +}
279 +
280 bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeout) {
281 worker_is_busy(WORKER_SENDER_CONNECTOR_JOB_CONNECTING);
282
@@ -342,104 +310,53 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
310 /* TODO: During the implementation of #7265 switch the set of variables to HOST_* and CONTAINER_* if the
311 version negotiation resulted in a high enough version.
312 */
345 - stream_encoded_t se;
346 - rrdpush_encode_variable(&se, host);
347 -
348 - char http[HTTP_HEADER_SIZE + 1];
349 - int eol = snprintfz(http, HTTP_HEADER_SIZE,
350 - "STREAM "
351 - "key=%s"
352 - "&hostname=%s"
353 - "&registry_hostname=%s"
354 - "&machine_guid=%s"
355 - "&update_every=%d"
356 - "&os=%s"
357 - "&timezone=%s"
358 - "&abbrev_timezone=%s"
359 - "&utc_offset=%d"
360 - "&hops=%d"
361 - "&ml_capable=%d"
362 - "&ml_enabled=%d"
363 - "&mc_version=%d"
364 - "&ver=%u"
365 - "&NETDATA_INSTANCE_CLOUD_TYPE=%s"
366 - "&NETDATA_INSTANCE_CLOUD_INSTANCE_TYPE=%s"
367 - "&NETDATA_INSTANCE_CLOUD_INSTANCE_REGION=%s"
368 - "&NETDATA_SYSTEM_OS_NAME=%s"
369 - "&NETDATA_SYSTEM_OS_ID=%s"
370 - "&NETDATA_SYSTEM_OS_ID_LIKE=%s"
371 - "&NETDATA_SYSTEM_OS_VERSION=%s"
372 - "&NETDATA_SYSTEM_OS_VERSION_ID=%s"
373 - "&NETDATA_SYSTEM_OS_DETECTION=%s"
374 - "&NETDATA_HOST_IS_K8S_NODE=%s"
375 - "&NETDATA_SYSTEM_KERNEL_NAME=%s"
376 - "&NETDATA_SYSTEM_KERNEL_VERSION=%s"
377 - "&NETDATA_SYSTEM_ARCHITECTURE=%s"
378 - "&NETDATA_SYSTEM_VIRTUALIZATION=%s"
379 - "&NETDATA_SYSTEM_VIRT_DETECTION=%s"
380 - "&NETDATA_SYSTEM_CONTAINER=%s"
381 - "&NETDATA_SYSTEM_CONTAINER_DETECTION=%s"
382 - "&NETDATA_CONTAINER_OS_NAME=%s"
383 - "&NETDATA_CONTAINER_OS_ID=%s"
384 - "&NETDATA_CONTAINER_OS_ID_LIKE=%s"
385 - "&NETDATA_CONTAINER_OS_VERSION=%s"
386 - "&NETDATA_CONTAINER_OS_VERSION_ID=%s"
387 - "&NETDATA_CONTAINER_OS_DETECTION=%s"
388 - "&NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT=%s"
389 - "&NETDATA_SYSTEM_CPU_FREQ=%s"
390 - "&NETDATA_SYSTEM_TOTAL_RAM=%s"
391 - "&NETDATA_SYSTEM_TOTAL_DISK_SIZE=%s"
392 - "&NETDATA_PROTOCOL_VERSION=%s"
393 - HTTP_1_1 HTTP_ENDL
394 - "User-Agent: %s/%s" HTTP_ENDL
395 - "Accept: */*" HTTP_HDR_END
396 - , string2str(host->stream.snd.api_key)
397 - , rrdhost_hostname(host)
398 - , rrdhost_registry_hostname(host)
399 - , host->machine_guid
400 - , default_rrd_update_every
401 - , rrdhost_os(host)
402 - , rrdhost_timezone(host)
403 - , rrdhost_abbrev_timezone(host)
404 - , host->utc_offset
405 - , s->hops
406 - , host->system_info->ml_capable
407 - , host->system_info->ml_enabled
408 - , host->system_info->mc_version
409 - , s->capabilities
410 - , (host->system_info->cloud_provider_type) ? host->system_info->cloud_provider_type : ""
411 - , (host->system_info->cloud_instance_type) ? host->system_info->cloud_instance_type : ""
412 - , (host->system_info->cloud_instance_region) ? host->system_info->cloud_instance_region : ""
413 - , se.os_name
414 - , se.os_id
415 - , (host->system_info->host_os_id_like) ? host->system_info->host_os_id_like : ""
416 - , se.os_version
417 - , (host->system_info->host_os_version_id) ? host->system_info->host_os_version_id : ""
418 - , (host->system_info->host_os_detection) ? host->system_info->host_os_detection : ""
419 - , (host->system_info->is_k8s_node) ? host->system_info->is_k8s_node : ""
420 - , se.kernel_name
421 - , se.kernel_version
422 - , (host->system_info->architecture) ? host->system_info->architecture : ""
423 - , (host->system_info->virtualization) ? host->system_info->virtualization : ""
424 - , (host->system_info->virt_detection) ? host->system_info->virt_detection : ""
425 - , (host->system_info->container) ? host->system_info->container : ""
426 - , (host->system_info->container_detection) ? host->system_info->container_detection : ""
427 - , (host->system_info->container_os_name) ? host->system_info->container_os_name : ""
428 - , (host->system_info->container_os_id) ? host->system_info->container_os_id : ""
429 - , (host->system_info->container_os_id_like) ? host->system_info->container_os_id_like : ""
430 - , (host->system_info->container_os_version) ? host->system_info->container_os_version : ""
431 - , (host->system_info->container_os_version_id) ? host->system_info->container_os_version_id : ""
432 - , (host->system_info->container_os_detection) ? host->system_info->container_os_detection : ""
433 - , (host->system_info->host_cores) ? host->system_info->host_cores : ""
434 - , (host->system_info->host_cpu_freq) ? host->system_info->host_cpu_freq : ""
435 - , (host->system_info->host_ram_total) ? host->system_info->host_ram_total : ""
436 - , (host->system_info->host_disk_space) ? host->system_info->host_disk_space : ""
437 - , STREAMING_PROTOCOL_VERSION
438 - , rrdhost_program_name(host)
439 - , rrdhost_program_version(host)
440 - );
441 - http[eol] = 0x00;
442 - rrdpush_clean_encoded(&se);
313 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
314 + buffer_strcat(wb, "STREAM ");
315 + buffer_key_value_urlencode(wb, "key", string2str(host->stream.snd.api_key));
316 + buffer_key_value_urlencode(wb, "&hostname", rrdhost_hostname(host));
317 + buffer_key_value_urlencode(wb, "&registry_hostname", rrdhost_registry_hostname(host));
318 + buffer_key_value_urlencode(wb, "&machine_guid", host->machine_guid);
319 + buffer_sprintf(wb, "&update_every=%d", default_rrd_update_every);
320 + buffer_key_value_urlencode(wb, "&os", rrdhost_os(host));
321 + buffer_key_value_urlencode(wb, "&timezone", rrdhost_timezone(host));
322 + buffer_key_value_urlencode(wb, "&abbrev_timezone", rrdhost_abbrev_timezone(host));
323 + buffer_sprintf(wb, "&utc_offset=%d", host->utc_offset);
324 + buffer_sprintf(wb, "&hops=%d", s->hops);
325 + buffer_sprintf(wb, "&ml_capable=%d", host->system_info->ml_capable);
326 + buffer_sprintf(wb, "&ml_enabled=%d", host->system_info->ml_enabled);
327 + buffer_sprintf(wb, "&mc_version=%d", host->system_info->mc_version);
328 + buffer_sprintf(wb, "&ver=%u", s->capabilities);
329 + buffer_key_value_urlencode(wb, "&NETDATA_INSTANCE_CLOUD_TYPE", host->system_info->cloud_provider_type);
330 + buffer_key_value_urlencode(wb, "&NETDATA_INSTANCE_CLOUD_INSTANCE_TYPE", host->system_info->cloud_instance_type);
331 + buffer_key_value_urlencode(wb, "&NETDATA_INSTANCE_CLOUD_INSTANCE_REGION", host->system_info->cloud_instance_region);
332 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_OS_NAME", host->system_info->host_os_name);
333 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_OS_ID", host->system_info->host_os_id);
334 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_OS_ID_LIKE", host->system_info->host_os_id_like);
335 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_OS_VERSION", host->system_info->host_os_version);
336 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_OS_VERSION_ID", host->system_info->host_os_version_id);
337 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_OS_DETECTION", host->system_info->host_os_detection);
338 + buffer_key_value_urlencode(wb, "&NETDATA_HOST_IS_K8S_NODE", host->system_info->is_k8s_node);
339 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_KERNEL_NAME", host->system_info->kernel_name);
340 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_KERNEL_VERSION", host->system_info->kernel_version);
341 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_ARCHITECTURE", host->system_info->architecture);
342 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_VIRTUALIZATION", host->system_info->virtualization);
343 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_VIRT_DETECTION", host->system_info->virt_detection);
344 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_CONTAINER", host->system_info->container);
345 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_CONTAINER_DETECTION", host->system_info->container_detection);
346 + buffer_key_value_urlencode(wb, "&NETDATA_CONTAINER_OS_NAME", host->system_info->container_os_name);
347 + buffer_key_value_urlencode(wb, "&NETDATA_CONTAINER_OS_ID", host->system_info->container_os_id);
348 + buffer_key_value_urlencode(wb, "&NETDATA_CONTAINER_OS_ID_LIKE", host->system_info->container_os_id_like);
349 + buffer_key_value_urlencode(wb, "&NETDATA_CONTAINER_OS_VERSION", host->system_info->container_os_version);
350 + buffer_key_value_urlencode(wb, "&NETDATA_CONTAINER_OS_VERSION_ID", host->system_info->container_os_version_id);
351 + buffer_key_value_urlencode(wb, "&NETDATA_CONTAINER_OS_DETECTION", host->system_info->container_os_detection);
352 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT", host->system_info->host_cores);
353 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_CPU_FREQ", host->system_info->host_cpu_freq);
354 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_TOTAL_RAM", host->system_info->host_ram_total);
355 + buffer_key_value_urlencode(wb, "&NETDATA_SYSTEM_TOTAL_DISK_SIZE", host->system_info->host_disk_space);
356 + buffer_key_value_urlencode(wb, "&NETDATA_PROTOCOL_VERSION", STREAMING_PROTOCOL_VERSION);
357 + buffer_strcat(wb, HTTP_1_1 HTTP_ENDL);
358 + buffer_sprintf(wb, "User-Agent: %s/%s" HTTP_ENDL, rrdhost_program_name(host), rrdhost_program_version(host));
359 + buffer_strcat(wb, "Accept: */*" HTTP_HDR_END);
360
361 if (s->parent_using_h2o && stream_connect_upgrade_prelude(host, s)) {
362 ND_LOG_STACK lgs[] = {
@@ -455,8 +372,8 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
372 return false;
373 }
374
458 - ssize_t len = (ssize_t)strlen(http);
459 - ssize_t bytes = nd_sock_send_timeout(&s->sock, http, len, 0, timeout);
375 + ssize_t len = (ssize_t)buffer_strlen(wb);
376 + ssize_t bytes = nd_sock_send_timeout(&s->sock, (void *)buffer_tostring(wb), len, 0, timeout);
377 if(bytes <= 0) { // timeout is 0
378 ND_LOG_STACK lgs[] = {
379 ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, STREAM_STATUS_TIMEOUT),
@@ -468,7 +385,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
385 nd_sock_close(&s->sock);
386
387 nd_log(NDLS_DAEMON, NDLP_ERR,
471 - "STREAM %s [send to %s]: failed to send HTTP header to remote netdata.",
388 + "STREAM CONNECT '%s' [to %s]: failed to send HTTP header to remote netdata.",
389 rrdhost_hostname(host), s->connected_to);
390
391 stream_parent_set_reconnect_delay(
@@ -476,7 +393,8 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
393 return false;
394 }
395
479 - bytes = nd_sock_recv_timeout(&s->sock, http, HTTP_HEADER_SIZE, 0, timeout);
396 + char response[4096];
397 + bytes = nd_sock_recv_timeout(&s->sock, response, sizeof(response) - 1, 0, timeout);
398 if(bytes <= 0) { // timeout is 0
399 nd_sock_close(&s->sock);
400
@@ -489,7 +407,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
407 worker_is_busy(WORKER_SENDER_CONNECTOR_JOB_DISCONNECT_TIMEOUT);
408
409 nd_log(NDLS_DAEMON, NDLP_ERR,
492 - "STREAM %s [send to %s]: remote netdata does not respond.",
410 + "STREAM CONNECT '%s' [to %s]: remote netdata does not respond.",
411 rrdhost_hostname(host), s->connected_to);
412
413 stream_parent_set_reconnect_delay(
@@ -497,21 +415,21 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
415
416 return false;
417 }
500 - http[bytes] = '\0';
418 + response[bytes] = '\0';
419
420 if(sock_setnonblock(s->sock.fd) < 0)
421 nd_log(NDLS_DAEMON, NDLP_WARNING,
504 - "STREAM %s [send to %s]: cannot set non-blocking mode for socket.",
422 + "STREAM CONNECT '%s' [to %s]: cannot set non-blocking mode for socket.",
423 rrdhost_hostname(host), s->connected_to);
424
425 sock_setcloexec(s->sock.fd);
426
427 if(sock_enlarge_out(s->sock.fd) < 0)
428 nd_log(NDLS_DAEMON, NDLP_WARNING,
511 - "STREAM %s [send to %s]: cannot enlarge the socket buffer.",
429 + "STREAM CONNECT '%s' [to %s]: cannot enlarge the socket buffer.",
430 rrdhost_hostname(host), s->connected_to);
431
514 - if(!stream_connect_validate_first_response(host, s, http, bytes)) {
432 + if(!stream_connect_validate_first_response(host, s, response, bytes)) {
433 nd_sock_close(&s->sock);
434 return false;
435 }
@@ -527,7 +445,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
445 ND_LOG_STACK_PUSH(lgs);
446
447 nd_log(NDLS_DAEMON, NDLP_DEBUG,
530 - "STREAM [connector] %s: connected to %s...",
448 + "STREAM CONNECT '%s' [to %s]: connected to parent...",
449 rrdhost_hostname(host), s->connected_to);
450
451 return true;
@@ -592,7 +510,7 @@ void stream_connector_requeue(struct sender_state *s) {
510 struct connector *sc = stream_connector_get(s);
511
512 nd_log(NDLS_DAEMON, NDLP_DEBUG,
595 - "STREAM [connector] [%s]: adding host in connector queue...",
513 + "STREAM CONNECT '%s' [to parent]: adding host in connector queue...",
514 rrdhost_hostname(s->host));
515
516 spinlock_lock(&sc->queue.spinlock);
@@ -608,13 +526,13 @@ void stream_connector_add(struct sender_state *s) {
526 // multiple threads may come here - only one should be able to pass through
527 stream_sender_lock(s);
528 if(!rrdhost_has_stream_sender_enabled(s->host) || !s->host->stream.snd.destination || !s->host->stream.snd.api_key) {
611 - nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM %s [send]: host has streaming disabled - not sending data to a parent.",
529 + nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM CONNECT '%s' [disabled]: host has streaming disabled - not sending data to a parent.",
530 rrdhost_hostname(s->host));
531 stream_sender_unlock(s);
532 return;
533 }
534 if(rrdhost_flag_check(s->host, RRDHOST_FLAG_STREAM_SENDER_ADDED)) {
617 - nd_log(NDLS_DAEMON, NDLP_DEBUG, "STREAM %s [send]: host has already added to sender - ignoring request",
535 + nd_log(NDLS_DAEMON, NDLP_DEBUG, "STREAM CONNECT '%s' [duplicate]: host has already added to sender - ignoring request.",
536 rrdhost_hostname(s->host));
537 stream_sender_unlock(s);
538 return;
@@ -632,7 +550,7 @@ void stream_connector_add(struct sender_state *s) {
550
551 static void stream_connector_remove(struct sender_state *s) {
552 nd_log(NDLS_DAEMON, NDLP_NOTICE,
635 - "STREAM [connector] [%s]: stopped streaming connector for host: %s",
553 + "STREAM CONNECT '%s' [stopped]: stopped streaming connector for host, reason: %s",
554 rrdhost_hostname(s->host), stream_handshake_error_to_string(s->exit.reason));
555
556 struct connector *sc = stream_connector_get(s);
@@ -658,8 +576,8 @@ static void *stream_connector_thread(void *ptr) {
576 worker_register_job_custom_metric(WORKER_SENDER_CONNECTOR_JOB_CANCELLED_NODES, "cancelled nodes", "nodes", WORKER_METRIC_ABSOLUTE);
577
578 unsigned job_id = 0;
661 -
579 while(!nd_thread_signaled_to_cancel() && service_running(SERVICE_STREAMING)) {
580 +
581 worker_is_idle();
582 job_id = completion_wait_for_a_job_with_timeout(&sc->completion, job_id, 1000);
583 size_t nodes = 0, connected_nodes = 0, failed_nodes = 0, cancelled_nodes = 0;
@@ -730,7 +648,7 @@ bool stream_connector_init(struct sender_state *s) {
648 if(!sc->thread) {
649 sc->id = (int8_t)(sc - connector_globals.connectors); // find the slot number
650 if(&connector_globals.connectors[sc->id] != sc)
733 - fatal("Connector ID and slot do not match!");
651 + fatal("STREAM CONNECT '%s': connector ID and slot do not match!", rrdhost_hostname(s->host));
652
653 spinlock_init(&sc->queue.spinlock);
654 completion_init(&sc->completion);
@@ -741,7 +659,9 @@ bool stream_connector_init(struct sender_state *s) {
659
660 sc->thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT, stream_connector_thread, sc);
661 if (!sc->thread)
744 - nd_log_daemon(NDLP_ERR, "STREAM connector: failed to create new thread for client.");
662 + nd_log_daemon(NDLP_ERR,
663 + "STREAM CONNECT '%s': failed to create new thread for client.",
664 + rrdhost_hostname(s->host));
665 }
666
667 spinlock_unlock(&spinlock);
src/streaming/stream-control.c new
+116
@@ -0,0 +1,116 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "stream-control.h"
4 +#include "stream.h"
5 +#include "replication.h"
6 +
7 +static struct {
8 + CACHE_LINE_PADDING();
9 +
10 + uint32_t backfill_runners;
11 +
12 + CACHE_LINE_PADDING();
13 +
14 + uint32_t replication_runners;
15 +
16 + CACHE_LINE_PADDING();
17 +
18 + uint32_t user_data_queries_runners;
19 +
20 + CACHE_LINE_PADDING();
21 +
22 + uint32_t user_weights_queries_runners;
23 +
24 + CACHE_LINE_PADDING();
25 +} sc;
26 +
27 +// --------------------------------------------------------------------------------------------------------------------
28 +// backfilling
29 +
30 +static uint32_t backfill_runners(void) {
31 + return __atomic_load_n(&sc.backfill_runners, __ATOMIC_RELAXED);
32 +}
33 +
34 +void stream_control_backfill_query_started(void) {
35 + __atomic_add_fetch(&sc.backfill_runners, 1, __ATOMIC_RELAXED);
36 +}
37 +
38 +void stream_control_backfill_query_finished(void) {
39 + __atomic_sub_fetch(&sc.backfill_runners, 1, __ATOMIC_RELAXED);
40 +}
41 +
42 +// --------------------------------------------------------------------------------------------------------------------
43 +// replication
44 +
45 +static uint32_t replication_runners(void) {
46 + return __atomic_load_n(&sc.replication_runners, __ATOMIC_RELAXED);
47 +}
48 +
49 +void stream_control_replication_query_started(void) {
50 + __atomic_add_fetch(&sc.replication_runners, 1, __ATOMIC_RELAXED);
51 +}
52 +
53 +void stream_control_replication_query_finished(void) {
54 + __atomic_sub_fetch(&sc.replication_runners, 1, __ATOMIC_RELAXED);
55 +}
56 +
57 +// --------------------------------------------------------------------------------------------------------------------
58 +// user data queries
59 +
60 +static uint32_t user_data_query_runners(void) {
61 + return __atomic_load_n(&sc.user_data_queries_runners, __ATOMIC_RELAXED);
62 +}
63 +
64 +void stream_control_user_data_query_started(void) {
65 + __atomic_add_fetch(&sc.user_data_queries_runners, 1, __ATOMIC_RELAXED);
66 +}
67 +
68 +void stream_control_user_data_query_finished(void) {
69 + __atomic_sub_fetch(&sc.user_data_queries_runners, 1, __ATOMIC_RELAXED);
70 +}
71 +
72 +// --------------------------------------------------------------------------------------------------------------------
73 +// user weights queries
74 +
75 +static uint32_t user_weights_query_runners(void) {
76 + return __atomic_load_n(&sc.user_weights_queries_runners, __ATOMIC_RELAXED);
77 +}
78 +
79 +void stream_control_user_weights_query_started(void) {
80 + __atomic_add_fetch(&sc.user_weights_queries_runners, 1, __ATOMIC_RELAXED);
81 +}
82 +
83 +void stream_control_user_weights_query_finished(void) {
84 + __atomic_sub_fetch(&sc.user_weights_queries_runners, 1, __ATOMIC_RELAXED);
85 +}
86 +
87 +// --------------------------------------------------------------------------------------------------------------------
88 +// consumer API
89 +
90 +bool stream_control_ml_should_be_running(void) {
91 + return backfill_runners() == 0 &&
92 + replication_runners() == 0 &&
93 + user_data_query_runners() == 0 &&
94 + user_weights_query_runners() == 0;
95 +}
96 +
97 +bool stream_control_children_should_be_accepted(void) {
98 + // we should not check for replication here.
99 + // replication benefits from multiple nodes (merges the extents)
100 + // and also the nodes should be close in time in the db
101 + // - checking for replication leaves the last few nodes locked-out (since all the others are replicating)
102 +
103 + return backfill_runners() == 0;
104 +}
105 +
106 +bool stream_control_replication_should_be_running(void) {
107 + return backfill_runners() == 0 &&
108 + user_data_query_runners() == 0 &&
109 + user_weights_query_runners() == 0;
110 +}
111 +
112 +bool stream_control_health_should_be_running(void) {
113 + return backfill_runners() == 0 &&
114 + replication_runners() == 0 &&
115 + (user_data_query_runners() + user_weights_query_runners()) <= 1;
116 +}
src/streaming/stream-control.h new
+29
@@ -0,0 +1,29 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STREAM_CONTROL_H
4 +#define NETDATA_STREAM_CONTROL_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +#define STREAM_CONTROL_SLEEP_UT (10 * USEC_PER_MS + os_random(10 * USEC_PER_MS))
9 +
10 +#define stream_control_throttle() microsleep(STREAM_CONTROL_SLEEP_UT)
11 +
12 +void stream_control_backfill_query_started(void);
13 +void stream_control_backfill_query_finished(void);
14 +
15 +void stream_control_replication_query_started(void);
16 +void stream_control_replication_query_finished(void);
17 +
18 +void stream_control_user_weights_query_started(void);
19 +void stream_control_user_weights_query_finished(void);
20 +
21 +void stream_control_user_data_query_started(void);
22 +void stream_control_user_data_query_finished(void);
23 +
24 +bool stream_control_ml_should_be_running(void);
25 +bool stream_control_children_should_be_accepted(void);
26 +bool stream_control_replication_should_be_running(void);
27 +bool stream_control_health_should_be_running(void);
28 +
29 +#endif //NETDATA_STREAM_CONTROL_H
src/streaming/stream-parents.c
+29 -24
@@ -1,6 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-sender-internals.h"
4 +#include "replication.h"
5
6 #define TIME_TO_CONSIDER_PARENTS_SIMILAR 120
7
@@ -150,7 +151,7 @@ void rrdhost_stream_parents_to_json(BUFFER *wb, RRDHOST_STATUS *s) {
151 STREAM_PARENT *d;
152 for (d = s->host->stream.snd.parents.all; d; d = d->next) {
153 buffer_json_add_array_item_object(wb);
153 - buffer_json_member_add_uint64(wb, "attempts", d->attempts);
154 + buffer_json_member_add_uint64(wb, "attempts", d->attempts + 1);
155 {
156 if (d->ssl) {
157 snprintfz(buf, sizeof(buf) - 1, "%s:SSL", string2str(d->destination));
@@ -305,6 +306,10 @@ int stream_info_to_json_v1(BUFFER *wb, const char *machine_guid) {
306 buffer_json_member_add_uint64(wb, "nonce", os_random32());
307
308 if(ret == HTTP_RESP_OK) {
309 + if((status.ingest.status == RRDHOST_INGEST_STATUS_ARCHIVED || status.ingest.status == RRDHOST_INGEST_STATUS_OFFLINE) &&
310 + !stream_control_children_should_be_accepted())
311 + status.ingest.status = RRDHOST_INGEST_STATUS_INITIALIZING;
312 +
313 buffer_json_member_add_string(wb, "db_status", rrdhost_db_status_to_string(status.db.status));
314 buffer_json_member_add_string(wb, "db_liveness", rrdhost_db_liveness_to_string(status.db.liveness));
315 buffer_json_member_add_string(wb, "ingest_type", rrdhost_ingest_type_to_string(status.ingest.type));
@@ -375,7 +380,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
380 rrdhost_program_version(localhost));
381
382 nd_log(NDLS_DAEMON, NDLP_DEBUG,
378 - "STREAM PARENTS of %s: fetching stream info from '%s'...",
383 + "STREAM PARENTS '%s': fetching stream info from '%s'...",
384 hostname, string2str(d->destination));
385
386 // Establish connection
@@ -384,7 +389,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
389 d->selection.info = false;
390 stream_parent_nd_sock_error_to_reason(d, &sock);
391 nd_log(NDLS_DAEMON, NDLP_WARNING,
387 - "STREAM PARENTS of %s: failed to connect for stream info to '%s': %s",
392 + "STREAM PARENTS '%s': failed to connect for stream info to '%s': %s",
393 hostname, string2str(d->destination),
394 ND_SOCK_ERROR_2str(sock.error));
395 return false;
@@ -396,7 +401,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
401 d->selection.info = false;
402 stream_parent_nd_sock_error_to_reason(d, &sock);
403 nd_log(NDLS_DAEMON, NDLP_WARNING,
399 - "STREAM PARENTS of %s: failed to send stream info request to '%s': %s",
404 + "STREAM PARENTS '%s': failed to send stream info request to '%s': %s",
405 hostname, string2str(d->destination),
406 ND_SOCK_ERROR_2str(sock.error));
407 return false;
@@ -413,7 +418,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
418
419 if (remaining <= 1) {
420 nd_log(NDLS_DAEMON, NDLP_WARNING,
416 - "STREAM PARENTS of %s: stream info receive buffer is full while receiving response from '%s'",
421 + "STREAM PARENTS '%s': stream info receive buffer is full while receiving response from '%s'",
422 hostname, string2str(d->destination));
423 d->selection.info = false;
424 d->reason = STREAM_HANDSHAKE_INTERNAL_ERROR;
@@ -423,7 +428,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
428 ssize_t received = nd_sock_recv_timeout(&sock, buf + total_received, remaining - 1, 0, 5);
429 if (received <= 0) {
430 nd_log(NDLS_DAEMON, NDLP_WARNING,
426 - "STREAM PARENTS of %s: socket receive error while querying stream info on '%s' "
431 + "STREAM PARENTS '%s': socket receive error while querying stream info on '%s' "
432 "(total received %zu, payload received %zu, content length %zu): %s",
433 hostname, string2str(d->destination),
434 total_received, payload_received, content_length,
@@ -453,7 +458,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
458 char *content_length_ptr = strstr(buf, "Content-Length: ");
459 if (!content_length_ptr) {
460 nd_log(NDLS_DAEMON, NDLP_WARNING,
456 - "STREAM PARENTS of %s: stream info response from '%s' does not have a Content-Length",
461 + "STREAM PARENTS '%s': stream info response from '%s' does not have a Content-Length",
462 hostname, string2str(d->destination));
463
464 d->selection.info = false;
@@ -463,7 +468,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
468 content_length = strtoul(content_length_ptr + strlen("Content-Length: "), NULL, 10);
469 if (!content_length) {
470 nd_log(NDLS_DAEMON, NDLP_WARNING,
466 - "STREAM PARENTS of %s: stream info response from '%s' has invalid Content-Length",
471 + "STREAM PARENTS '%s': stream info response from '%s' has invalid Content-Length",
472 hostname, string2str(d->destination));
473
474 d->selection.info = false;
@@ -479,7 +484,7 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
484 d->selection.info = false;
485 d->reason = STREAM_HANDSHAKE_NO_STREAM_INFO;
486 nd_log(NDLS_DAEMON, NDLP_WARNING,
482 - "STREAM PARENTS of %s: failed to parse stream info response from '%s', JSON data: %s",
487 + "STREAM PARENTS '%s': failed to parse stream info response from '%s', JSON data: %s",
488 hostname, string2str(d->destination), payload_start);
489 return false;
490 }
@@ -490,14 +495,14 @@ static bool stream_info_fetch(STREAM_PARENT *d, const char *uuid, int default_po
495 d->selection.info = false;
496 d->reason = STREAM_HANDSHAKE_NO_STREAM_INFO;
497 nd_log(NDLS_DAEMON, NDLP_WARNING,
493 - "STREAM PARENTS of %s: failed to extract fields from JSON stream info response from '%s': %s",
498 + "STREAM PARENTS '%s': failed to extract fields from JSON stream info response from '%s': %s",
499 hostname, string2str(d->destination),
500 buffer_tostring(error));
501 return false;
502 }
503
504 nd_log(NDLS_DAEMON, NDLP_DEBUG,
500 - "STREAM PARENTS of %s: received stream_info data from '%s': "
505 + "STREAM PARENTS '%s': received stream_info data from '%s': "
506 "status: %d, nodes: %zu, receivers: %zu, first_time_s: %ld, last_time_s: %ld, "
507 "db status: %s, db liveness: %s, ingest type: %s, ingest status: %s",
508 hostname, string2str(d->destination),
@@ -554,7 +559,7 @@ bool stream_parent_connect_to_one_unsafe(
559
560 // do we have any parents?
561 if(!size) {
557 - nd_log(NDLS_DAEMON, NDLP_DEBUG, "STREAM PARENTS of %s: no parents configured", rrdhost_hostname(host));
562 + nd_log(NDLS_DAEMON, NDLP_DEBUG, "STREAM PARENTS '%s': no parents configured", rrdhost_hostname(host));
563 return false;
564 }
565
@@ -581,7 +586,7 @@ bool stream_parent_connect_to_one_unsafe(
586 if (d->postpone_until_ut > now_ut) {
587 skipped_but_useful++;
588 nd_log(NDLS_DAEMON, NDLP_DEBUG,
584 - "STREAM PARENTS of %s: skipping useful parent '%s': POSTPONED FOR %ld SECS MORE: %s",
589 + "STREAM PARENTS '%s': skipping useful parent '%s': POSTPONED FOR %ld SECS MORE: %s",
590 rrdhost_hostname(host),
591 string2str(d->destination),
592 (time_t)((d->postpone_until_ut - now_ut) / USEC_PER_SEC),
@@ -603,7 +608,7 @@ bool stream_parent_connect_to_one_unsafe(
608 d->banned_permanently = true;
609 skipped_not_useful++;
610 nd_log(NDLS_DAEMON, NDLP_NOTICE,
606 - "STREAM PARENTS of %s: destination '%s' is banned permanently because it is the origin server",
611 + "STREAM PARENTS '%s': destination '%s' is banned permanently because it is the origin server",
612 rrdhost_hostname(host), string2str(d->destination));
613 continue;
614 }
@@ -631,7 +636,7 @@ bool stream_parent_connect_to_one_unsafe(
636 d->banned_for_this_session = true;
637 skipped_not_useful++;
638 nd_log(NDLS_DAEMON, NDLP_NOTICE,
634 - "STREAM PARENTS of %s: destination '%s' is banned for this session, because it is in our path before us.",
639 + "STREAM PARENTS '%s': destination '%s' is banned for this session, because it is in our path before us.",
640 rrdhost_hostname(host), string2str(d->destination));
641 continue;
642 }
@@ -648,7 +653,7 @@ bool stream_parent_connect_to_one_unsafe(
653 if(skip) {
654 skipped_but_useful++;
655 nd_log(NDLS_DAEMON, NDLP_DEBUG,
651 - "STREAM PARENTS of %s: skipping useful parent '%s': %s",
656 + "STREAM PARENTS '%s': skipping useful parent '%s': %s",
657 rrdhost_hostname(host),
658 string2str(d->destination),
659 stream_handshake_error_to_string(d->reason));
@@ -664,7 +669,7 @@ bool stream_parent_connect_to_one_unsafe(
669 // can we use any parent?
670 if(!count) {
671 nd_log(NDLS_DAEMON, NDLP_DEBUG,
667 - "STREAM PARENTS of %s: no parents available (%zu skipped but useful, %zu skipped not useful)",
672 + "STREAM PARENTS '%s': no parents available (%zu skipped but useful, %zu skipped not useful)",
673 rrdhost_hostname(host),
674 skipped_but_useful, skipped_not_useful);
675 return false;
@@ -692,7 +697,7 @@ bool stream_parent_connect_to_one_unsafe(
697 // if we have only 1 similar, move on
698 if (similar == 1) {
699 nd_log(NDLS_DAEMON, NDLP_DEBUG,
695 - "STREAM PARENTS of %s: reordering keeps parent No %zu, '%s'",
700 + "STREAM PARENTS '%s': reordering keeps parent No %zu, '%s'",
701 rrdhost_hostname(host), base, string2str(array[base]->destination));
702 array[base]->selection.order = base + 1;
703 array[base]->selection.batch = batch + 1;
@@ -716,7 +721,7 @@ bool stream_parent_connect_to_one_unsafe(
721 SWAP(array[base], array[chosen]);
722
723 nd_log(NDLS_DAEMON, NDLP_DEBUG,
719 - "STREAM PARENTS of %s: random reordering of %zu similar parents (slots %zu to %zu), No %zu is '%s'",
724 + "STREAM PARENTS '%s': random reordering of %zu similar parents (slots %zu to %zu), No %zu is '%s'",
725 rrdhost_hostname(host),
726 similar, base, base + similar,
727 base, string2str(array[base]->destination));
@@ -743,7 +748,7 @@ bool stream_parent_connect_to_one_unsafe(
748 array[0]->selection.random = false;
749
750 nd_log(NDLS_DAEMON, NDLP_DEBUG,
746 - "STREAM PARENTS of %s: only 1 parent is available: '%s'",
751 + "STREAM PARENTS '%s': only 1 parent is available: '%s'",
752 rrdhost_hostname(host), string2str(array[0]->destination));
753 }
754
@@ -760,7 +765,7 @@ bool stream_parent_connect_to_one_unsafe(
765 }
766
767 nd_log(NDLS_DAEMON, NDLP_DEBUG,
763 - "STREAM PARENTS of %s: connecting to '%s' (default port: %d, parent %zu of %zu)...",
768 + "STREAM PARENTS '%s': connecting to '%s' (default port: %d, parent %zu of %zu)...",
769 rrdhost_hostname(host), string2str(d->destination), default_port,
770 i + 1, count);
771
@@ -788,7 +793,7 @@ bool stream_parent_connect_to_one_unsafe(
793 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(host->stream.snd.parents.all, d, prev, next);
794
795 nd_log(NDLS_DAEMON, NDLP_DEBUG,
791 - "STREAM PARENTS of %s: connected to '%s' (default port: %d, fd %d)...",
796 + "STREAM PARENTS '%s': connected to '%s' (default port: %d, fd %d)...",
797 rrdhost_hostname(host), string2str(d->destination), default_port,
798 sender_sock->fd);
799
@@ -798,7 +803,7 @@ bool stream_parent_connect_to_one_unsafe(
803 else {
804 stream_parent_nd_sock_error_to_reason(d, sender_sock);
805 nd_log(NDLS_DAEMON, NDLP_DEBUG,
801 - "STREAM PARENTS of %s: stream connection to '%s' failed (default port: %d): %s",
806 + "STREAM PARENTS '%s': stream connection to '%s' failed (default port: %d): %s",
807 rrdhost_hostname(host),
808 string2str(d->destination), default_port,
809 ND_SOCK_ERROR_2str(sender_sock->error));
@@ -854,7 +859,7 @@ static bool stream_parent_add_one_unsafe(char *entry, void *data) {
859
860 t->count++;
861 nd_log(NDLS_DAEMON, NDLP_DEBUG,
857 - "STREAM PARENTS of %s: added streaming destination No %d: '%s'",
862 + "STREAM PARENTS '%s': added streaming destination No %d: '%s'",
863 rrdhost_hostname(t->host), t->count, string2str(d->destination));
864
865 return false; // we return false, so that we will get all defined destinations
src/streaming/stream-path.c
+8 -5
@@ -237,7 +237,7 @@ void stream_path_send_to_child(RRDHOST *host) {
237
238 rrdhost_receiver_lock(host);
239 if(stream_has_capability(host->receiver, STREAM_CAP_PATHS) &&
240 - !rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED)) {
240 + rrdhost_flag_check(host, RRDHOST_FLAG_COLLECTOR_ONLINE)) {
241
242 CLEAN_BUFFER *wb = buffer_create(0, NULL);
243 buffer_sprintf(wb, PLUGINSD_KEYWORD_JSON " " PLUGINSD_KEYWORD_JSON_CMD_STREAM_PATH "\n%s\n" PLUGINSD_KEYWORD_JSON_END "\n", buffer_tostring(payload));
@@ -317,7 +317,7 @@ static bool parse_single_path(json_object *jobj, const char *path, STREAM_PATH *
317 }
318
319 if(p->hops < 0) {
320 - buffer_strcat(error, "hops cannot be negative");
320 + buffer_strcat(error, "hops cannot be negative (probably the child disconnected from the Netdata before us");
321 return false;
322 }
323
@@ -360,7 +360,8 @@ bool stream_path_set_from_json(RRDHOST *host, const char *json, bool from_parent
360 CLEAN_JSON_OBJECT *jobj = json_tokener_parse(json);
361 if(!jobj) {
362 nd_log(NDLS_DAEMON, NDLP_ERR,
363 - "STREAM PATH: Cannot parse json: %s", json);
363 + "STREAM PATH '%s': Cannot parse json: %s",
364 + rrdhost_hostname(host), json);
365 return false;
366 }
367
@@ -381,14 +382,16 @@ bool stream_path_set_from_json(RRDHOST *host, const char *json, bool from_parent
382 json_object *joption = json_object_array_get_idx(_jarray, i);
383 if (!json_object_is_type(joption, json_type_object)) {
384 nd_log(NDLS_DAEMON, NDLP_ERR,
384 - "STREAM PATH: Array item No %zu is not an object: %s", i, json);
385 + "STREAM PATH '%s': Array item No %zu is not an object: %s",
386 + rrdhost_hostname(host), i, json);
387 continue;
388 }
389
390 if(!parse_single_path(joption, "", &host->stream.path.array[host->stream.path.used], error)) {
391 stream_path_cleanup(&host->stream.path.array[host->stream.path.used]);
392 nd_log(NDLS_DAEMON, NDLP_ERR,
391 - "STREAM PATH: Array item No %zu cannot be parsed: %s: %s", i, buffer_tostring(error), json);
393 + "STREAM PATH '%s': Array item No %zu cannot be parsed: %s: %s",
394 + rrdhost_hostname(host), i, buffer_tostring(error), json);
395 }
396 else
397 host->stream.path.used++;
src/streaming/stream-receiver-connection.c
+34 -21
@@ -4,6 +4,7 @@
4 #include "stream-thread.h"
5 #include "stream-receiver-internals.h"
6 #include "web/server/h2o/http_server.h"
7 +#include "replication.h"
8
9 // --------------------------------------------------------------------------------------------------------------------
10
@@ -25,8 +26,9 @@ void stream_receiver_log_status(struct receiver_state *rpt, const char *msg, con
26 , (rpt->machine_guid && *rpt->machine_guid) ? rpt->machine_guid : ""
27 , msg);
28
28 - nd_log(NDLS_DAEMON, priority, "STREAM RECEIVE '%s': %s %s%s%s"
29 + nd_log(NDLS_DAEMON, priority, "STREAM RECEIVE '%s' [from [%s]:%s]: %s %s%s%s"
30 , (rpt->hostname && *rpt->hostname) ? rpt->hostname : ""
31 + , rpt->client_ip, rpt->client_port
32 , msg
33 , rpt->exit.reason != STREAM_HANDSHAKE_NEVER?" (":""
34 , stream_handshake_error_to_string(rpt->exit.reason)
@@ -142,30 +144,41 @@ static bool stream_receiver_send_first_response(struct receiver_state *rpt) {
144 if(!host) {
145 stream_receiver_log_status(
146 rpt,
145 - "failed to find/create host structure, rejecting connection",
147 + "rejecting streaming connection; failed to find or create the required host structure",
148 STREAM_STATUS_INTERNAL_SERVER_ERROR, NDLP_ERR);
149
150 stream_send_error_on_taken_over_connection(rpt, START_STREAMING_ERROR_INTERNAL_ERROR);
151 return false;
152 }
153 + // IMPORTANT: KEEP THIS FIRST AFTER CHECKING host RESPONSE!
154 + // THIS IS HOW WE KNOW THE system_info IS GONE NOW...
155 + // system_info has been consumed by the host structure
156 + rpt->system_info = NULL;
157
158 if (unlikely(rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_CONTEXT_LOAD))) {
159 stream_receiver_log_status(
160 rpt,
155 - "host is initializing, retry later",
161 + "rejecting streaming connection; host is initializing, retry later",
162 STREAM_STATUS_INITIALIZATION_IN_PROGRESS, NDLP_NOTICE);
163
164 stream_send_error_on_taken_over_connection(rpt, START_STREAMING_ERROR_INITIALIZATION);
165 return false;
166 }
167
162 - // system_info has been consumed by the host structure
163 - rpt->system_info = NULL;
168 + if (unlikely(!stream_control_children_should_be_accepted())) {
169 + stream_receiver_log_status(
170 + rpt,
171 + "rejecting streaming connection; the system is backfilling higher tiers with high-resolution data, retry later",
172 + STREAM_STATUS_INITIALIZATION_IN_PROGRESS, NDLP_NOTICE);
173 +
174 + stream_send_error_on_taken_over_connection(rpt, START_STREAMING_ERROR_INITIALIZATION);
175 + return false;
176 + }
177
178 if(!rrdhost_set_receiver(host, rpt)) {
179 stream_receiver_log_status(
180 rpt,
168 - "host is already served by another receiver",
181 + "rejecting streaming connection; host is already served by another receiver",
182 STREAM_STATUS_DUPLICATE_RECEIVER, NDLP_INFO);
183
184 stream_send_error_on_taken_over_connection(rpt, START_STREAMING_ERROR_ALREADY_STREAMING);
@@ -174,7 +187,7 @@ static bool stream_receiver_send_first_response(struct receiver_state *rpt) {
187 }
188
189 #ifdef NETDATA_INTERNAL_CHECKS
177 - netdata_log_info("STREAM '%s' [receive from [%s]:%s]: "
190 + netdata_log_info("STREAM RECEIVE '%s' [from [%s]:%s]: "
191 "client willing to stream metrics for host '%s' with machine_guid '%s': "
192 "update every = %d, history = %d, memory mode = %s, health %s,%s"
193 , rpt->hostname
@@ -395,7 +408,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
408 if(!rpt->key || !*rpt->key) {
409 stream_receiver_log_status(
410 rpt,
398 - "request without an API key, rejecting connection",
411 + "rejecting streaming connection; request without an API key",
412 STREAM_STATUS_NO_API_KEY, NDLP_WARNING);
413
414 stream_receiver_free(rpt);
@@ -405,7 +418,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
418 if(!rpt->hostname || !*rpt->hostname) {
419 stream_receiver_log_status(
420 rpt,
408 - "request without a hostname, rejecting connection",
421 + "rejecting streaming connection; request without a hostname",
422 STREAM_STATUS_NO_HOSTNAME, NDLP_WARNING);
423
424 stream_receiver_free(rpt);
@@ -418,7 +431,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
431 if(!rpt->machine_guid || !*rpt->machine_guid) {
432 stream_receiver_log_status(
433 rpt,
421 - "request without a machine GUID, rejecting connection",
434 + "rejecting streaming connection; request without a machine UUID",
435 STREAM_STATUS_NO_MACHINE_GUID, NDLP_WARNING);
436
437 stream_receiver_free(rpt);
@@ -431,7 +444,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
444 if (regenerate_guid(rpt->key, buf) == -1) {
445 stream_receiver_log_status(
446 rpt,
434 - "API key is not a valid UUID (use the command uuidgen to generate one)",
447 + "rejecting streaming connection; API key is not a valid UUID (use the command uuidgen to generate one)",
448 STREAM_STATUS_INVALID_API_KEY, NDLP_WARNING);
449
450 stream_receiver_free(rpt);
@@ -441,7 +454,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
454 if (regenerate_guid(rpt->machine_guid, buf) == -1) {
455 stream_receiver_log_status(
456 rpt,
444 - "machine GUID is not a valid UUID",
457 + "rejecting streaming connection; machine UUID is not a valid UUID",
458 STREAM_STATUS_INVALID_MACHINE_GUID, NDLP_WARNING);
459
460 stream_receiver_free(rpt);
@@ -452,7 +465,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
465 if(!stream_conf_is_key_type(rpt->key, "api")) {
466 stream_receiver_log_status(
467 rpt,
455 - "API key is a machine GUID",
468 + "rejecting streaming connection; API key provided is a machine UUID (did you mix them up?)",
469 STREAM_STATUS_INVALID_API_KEY, NDLP_WARNING);
470
471 stream_receiver_free(rpt);
@@ -464,7 +477,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
477 if(!stream_conf_api_key_is_enabled(rpt->key, false)) {
478 stream_receiver_log_status(
479 rpt,
467 - "API key is not enabled",
480 + "rejecting streaming connection; API key is not enabled in stream.conf",
481 STREAM_STATUS_API_KEY_DISABLED, NDLP_WARNING);
482
483 stream_receiver_free(rpt);
@@ -474,7 +487,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
487 if(!stream_conf_api_key_allows_client(rpt->key, w->client_ip)) {
488 stream_receiver_log_status(
489 rpt,
477 - "API key is not allowed from this IP",
490 + "rejecting streaming connection; API key is not allowed from this IP",
491 STREAM_STATUS_NOT_ALLOWED_IP, NDLP_WARNING);
492
493 stream_receiver_free(rpt);
@@ -484,7 +497,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
497 if (!stream_conf_is_key_type(rpt->machine_guid, "machine")) {
498 stream_receiver_log_status(
499 rpt,
487 - "machine GUID is an API key",
500 + "rejecting streaming connection; machine UUID is an API key (did you mix them up?)",
501 STREAM_STATUS_INVALID_MACHINE_GUID, NDLP_WARNING);
502
503 stream_receiver_free(rpt);
@@ -496,7 +509,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
509 if(!stream_conf_api_key_is_enabled(rpt->machine_guid, true)) {
510 stream_receiver_log_status(
511 rpt,
499 - "machine GUID is not enabled",
512 + "rejecting streaming connection; machine UUID is not enabled in stream.conf",
513 STREAM_STATUS_MACHINE_GUID_DISABLED, NDLP_WARNING);
514
515 stream_receiver_free(rpt);
@@ -506,7 +519,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
519 if(!stream_conf_api_key_allows_client(rpt->machine_guid, w->client_ip)) {
520 stream_receiver_log_status(
521 rpt,
509 - "machine GUID is not allowed from this IP",
522 + "rejecting streaming connection; machine UUID is not allowed from this IP",
523 STREAM_STATUS_NOT_ALLOWED_IP, NDLP_WARNING);
524
525 stream_receiver_free(rpt);
@@ -518,7 +531,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
531
532 stream_receiver_log_status(
533 rpt,
521 - "machine GUID is my own",
534 + "rejecting streaming connection; machine UUID is my own",
535 STREAM_STATUS_LOCALHOST, NDLP_DEBUG);
536
537 char initial_response[HTTP_HEADER_SIZE + 1];
@@ -551,7 +564,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
564
565 char msg[100 + 1];
566 snprintfz(msg, sizeof(msg) - 1,
554 - "rate limit, will accept new connection in %ld secs",
567 + "rejecting streaming connection; rate limit, will accept new connection in %ld secs",
568 (long)(web_client_streaming_rate_t - (now - last_stream_accepted_t)));
569
570 stream_receiver_log_status(rpt, msg, STREAM_STATUS_RATE_LIMIT, NDLP_NOTICE);
@@ -616,7 +629,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
629
630 char msg[200 + 1];
631 snprintfz(msg, sizeof(msg) - 1,
619 - "multiple connections for same host, "
632 + "rejecting streaming connection; multiple connections for same host, "
633 "old connection was last used %ld secs ago%s",
634 age, receiver_stale ? " (signaled old receiver to stop)" : " (new connection not accepted)");
635
src/streaming/stream-receiver-internals.h
+2 -1
@@ -35,6 +35,8 @@ struct receiver_state {
35 struct buffered_reader reader;
36
37 struct {
38 + bool draining_input; // used exclusively by the stream thread
39 +
40 // The parser pointer is safe to read and use, only when having the host receiver lock.
41 // Without this lock, the data pointed by the pointer may vanish randomly.
42 // Also, since the receiver sets it when it starts, it should be read with
@@ -88,7 +90,6 @@ void stream_receiver_log_status(struct receiver_state *rpt, const char *msg, con
90 void stream_receiver_free(struct receiver_state *rpt);
91 bool stream_receiver_signal_to_stop_and_wait(RRDHOST *host, STREAM_HANDSHAKE reason);
92
91 -ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE type);
93 void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcode msg);
94 void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state *rpt, struct stream_opcode *msg);
95
src/streaming/stream-receiver.c
+129 -87
@@ -147,14 +147,18 @@ static inline decompressor_status_t receiver_feed_decompressor(struct receiver_s
147 stream_decompressor_start(&r->thread.compressed.decompressor, buf + start, signature_size);
148
149 if (unlikely(!compressed_message_size)) {
150 - nd_log(NDLS_DAEMON, NDLP_ERR, "multiplexed uncompressed data in compressed stream!");
150 + nd_log(NDLS_DAEMON, NDLP_ERR,
151 + "STREAM RECEIVE[x] '%s' [from [%s]:%s]: multiplexed uncompressed data in compressed stream!",
152 + rrdhost_hostname(r->host), r->client_ip, r->client_port);
153 return DECOMPRESS_FAILED;
154 }
155
156 if(unlikely(compressed_message_size > COMPRESSION_MAX_MSG_SIZE)) {
157 nd_log(NDLS_DAEMON, NDLP_ERR,
156 - "received a compressed message of %zu bytes, which is bigger than the max compressed message "
158 + "STREAM RECEIVE[x] '%s' [from [%s]:%s]: received a compressed message of %zu bytes, "
159 + "which is bigger than the max compressed message "
160 "size supported of %zu. Ignoring message.",
161 + rrdhost_hostname(r->host), r->client_ip, r->client_port,
162 compressed_message_size, (size_t)COMPRESSION_MAX_MSG_SIZE);
163 return DECOMPRESS_FAILED;
164 }
@@ -169,7 +173,9 @@ static inline decompressor_status_t receiver_feed_decompressor(struct receiver_s
173 stream_decompress(&r->thread.compressed.decompressor, buf + start + signature_size, compressed_message_size);
174
175 if (unlikely(!bytes_to_parse)) {
172 - nd_log(NDLS_DAEMON, NDLP_ERR, "no bytes to parse.");
176 + nd_log(NDLS_DAEMON, NDLP_ERR,
177 + "STREAM RECEIVE[x] '%s' [from [%s]:%s]: no bytes to decompress.",
178 + rrdhost_hostname(r->host), r->client_ip, r->client_port);
179 return DECOMPRESS_FAILED;
180 }
181
@@ -259,9 +265,9 @@ void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state
265 STREAM_CIRCULAR_BUFFER_STATS stats = *stream_circular_buffer_stats_unsafe(rpt->thread.send_to_child.scb);
266 spinlock_unlock(&rpt->thread.send_to_child.spinlock);
267 nd_log(NDLS_DAEMON, NDLP_ERR,
262 - "STREAM RECEIVE[%zu] %s [from %s]: send buffer is full (buffer size %u, max %u, used %u, available %u). "
268 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: send buffer is full (buffer size %u, max %u, used %u, available %u). "
269 "Restarting connection.",
264 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip,
270 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port,
271 stats.bytes_size, stats.bytes_max_size, stats.bytes_outstanding, stats.bytes_available);
272
273 stream_receiver_remove(sth, rpt, "receiver send buffer overflow");
@@ -272,7 +278,7 @@ void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state
278 "STREAM RECEIVE[%zu]: invalid msg id %u", sth->id, (unsigned)msg->opcode);
279 }
280
275 -ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE type) {
281 +static ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE type) {
282 struct receiver_state *rpt = data;
283 if(!rpt || rpt->thread.meta.type != POLLFD_TYPE_RECEIVER || !rpt->thread.send_to_child.scb)
284 return 0;
@@ -286,7 +292,8 @@ ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE type) {
292
293 size_t size = strlen(txt);
294 ssize_t rc = (ssize_t)size;
289 - if(!stream_circular_buffer_add_unsafe(scb, txt, size, size, type)) {
295 + if(!stream_circular_buffer_add_unsafe(scb, txt, size, size, type, true)) {
296 + // should never happen, because of autoscaling
297 msg.opcode = STREAM_OPCODE_RECEIVER_BUFFER_OVERFLOW;
298 rc = -1;
299 }
@@ -315,14 +322,10 @@ static void streaming_parser_init(struct receiver_state *rpt) {
322 // put the client IP and port into the buffers used by plugins.d
323 {
324 char buf[CONFIG_MAX_NAME];
318 - snprintfz(buf, sizeof(buf), "%s:%s", rpt->client_ip, rpt->client_port);
325 + snprintfz(buf, sizeof(buf), "[%s]:%s", rpt->client_ip, rpt->client_port);
326 string_freez(rpt->thread.cd.id);
327 rpt->thread.cd.id = string_strdupz(buf);
321 - }
328
323 - {
324 - char buf[FILENAME_MAX + 1];
325 - snprintfz(buf, sizeof(buf), "%s:%s", rpt->client_ip, rpt->client_port);
329 string_freez(rpt->thread.cd.filename);
330 rpt->thread.cd.filename = string_strdupz(buf);
331
@@ -384,6 +387,23 @@ static void streaming_parser_init(struct receiver_state *rpt) {
387
388 // --------------------------------------------------------------------------------------------------------------------
389
390 +static void stream_receive_log_database_gap(struct receiver_state *rpt) {
391 + RRDHOST *host = rpt->host;
392 +
393 + time_t now = now_realtime_sec();
394 + time_t last_db_entry = 0;
395 + rrdhost_retention(host, now, false, NULL, &last_db_entry);
396 +
397 + if(now < last_db_entry)
398 + last_db_entry = now;
399 +
400 + char buf[128];
401 + duration_snprintf(buf, sizeof(buf), now - last_db_entry, "s", true);
402 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
403 + "STREAM RECEIVE '%s' [from [%s]:%s]: node connected; last sample in the database %s ago",
404 + rrdhost_hostname(host), rpt->client_ip, rpt->client_port, buf);
405 +}
406 +
407 void stream_receiver_move_queue_to_running_unsafe(struct stream_thread *sth) {
408 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
409
@@ -404,8 +424,8 @@ void stream_receiver_move_queue_to_running_unsafe(struct stream_thread *sth) {
424 ND_LOG_STACK_PUSH(lgs);
425
426 nd_log(NDLS_DAEMON, NDLP_DEBUG,
407 - "STREAM RECEIVE[%zu] [%s]: moving host from receiver queue to receiver running...",
408 - sth->id, rrdhost_hostname(rpt->host));
427 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: moving host from receiver queue to receiver running...",
428 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
429
430 rpt->host->stream.rcv.status.tid = gettid_cached();
431 rpt->thread.meta.type = POLLFD_TYPE_RECEIVER;
@@ -413,9 +433,6 @@ void stream_receiver_move_queue_to_running_unsafe(struct stream_thread *sth) {
433
434 spinlock_lock(&rpt->thread.send_to_child.spinlock);
435 rpt->thread.send_to_child.scb = stream_circular_buffer_create();
416 -
417 - // this should be big enough to fit all the replies to the replication requests we may receive in a batch
418 - stream_circular_buffer_set_max_size_unsafe(rpt->thread.send_to_child.scb, 100 * 1024 * 1024, true);
436 rpt->thread.send_to_child.msg.thread_slot = (int32_t)sth->id;
437 rpt->thread.send_to_child.msg.session = os_random32();
438 rpt->thread.send_to_child.msg.meta = &rpt->thread.meta;
@@ -430,7 +447,12 @@ void stream_receiver_move_queue_to_running_unsafe(struct stream_thread *sth) {
447 rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
448
449 if(!nd_poll_add(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
433 - nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to add receiver socket to nd_poll()");
450 + nd_log(NDLS_DAEMON, NDLP_ERR,
451 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]:"
452 + "Failed to add receiver socket to nd_poll()",
453 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
454 +
455 + stream_receive_log_database_gap(rpt);
456
457 // keep this last, since it sends commands back to the child
458 streaming_parser_init(rpt);
@@ -508,6 +530,8 @@ static void stream_receiver_remove(struct stream_thread *sth, struct receiver_st
530
531 static ssize_t
532 stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt, PARSER *parser, bool *removed) {
533 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
534 +
535 ssize_t rc;
536 if(rpt->thread.compressed.enabled) {
537 rc = receiver_read_compressed(rpt);
@@ -589,10 +613,10 @@ stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt
613 }
614
615 // process poll() events for streaming receivers
592 -void stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut)
616 +// returns true when the receiver is still there, false if it removed it
617 +bool stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut)
618 {
594 - internal_fatal(
595 - sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
619 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
620
621 PARSER *parser = __atomic_load_n(&rpt->thread.parser, __ATOMIC_RELAXED);
622 ND_LOG_STACK lgs[] = {
@@ -612,7 +636,7 @@ void stream_receive_process_poll_events(struct stream_thread *sth, struct receiv
636 if (receiver_should_stop(rpt)) {
637 receiver_set_exit_reason(rpt, rpt->exit.reason, false);
638 stream_receiver_remove(sth, rpt, "received stop signal");
615 - return;
639 + return false;
640 }
641
642 if (unlikely(events & (ND_POLL_ERROR | ND_POLL_HUP | ND_POLL_INVALID))) {
@@ -631,109 +655,122 @@ void stream_receive_process_poll_events(struct stream_thread *sth, struct receiv
655
656 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR);
657
634 - nd_log(
635 - NDLS_DAEMON,
636 - NDLP_ERR,
637 - "STREAM RECEIVE[%zu] %s [from %s]: %s - closing connection",
638 - sth->id,
639 - rrdhost_hostname(rpt->host),
640 - rpt->client_ip,
641 - error);
658 + nd_log(NDLS_DAEMON, NDLP_ERR,
659 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: %s - closing connection",
660 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, error);
661
662 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, false);
663 stream_receiver_remove(sth, rpt, error);
645 - return;
664 + return false;
665 }
666
667 if (events & ND_POLL_WRITE) {
668 worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
669
651 - if (spinlock_trylock(&rpt->thread.send_to_child.spinlock)) {
652 - const char *disconnect_reason = NULL;
653 - STREAM_HANDSHAKE reason;
654 -
655 - char *chunk;
656 - STREAM_CIRCULAR_BUFFER *scb = rpt->thread.send_to_child.scb;
657 - STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(scb);
658 - size_t outstanding = stream_circular_buffer_get_unsafe(scb, &chunk);
659 - ssize_t rc = write_stream(rpt, chunk, outstanding);
660 - if (likely(rc > 0)) {
661 - stream_circular_buffer_del_unsafe(scb, rc);
662 - if (!stats->bytes_outstanding) {
663 - if (!nd_poll_upd(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
664 - nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM RECEIVE: cannot update nd_poll()");
665 -
666 - // recreate the circular buffer if we have to
667 - stream_circular_buffer_recreate_timed_unsafe(rpt->thread.send_to_child.scb, now_ut, false);
670 + bool stop = false;
671 + while(!stop) {
672 + if (spinlock_trylock(&rpt->thread.send_to_child.spinlock)) {
673 + const char *disconnect_reason = NULL;
674 + STREAM_HANDSHAKE reason;
675 +
676 + char *chunk;
677 + STREAM_CIRCULAR_BUFFER *scb = rpt->thread.send_to_child.scb;
678 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(scb);
679 + size_t outstanding = stream_circular_buffer_get_unsafe(scb, &chunk);
680 + ssize_t rc = write_stream(rpt, chunk, outstanding);
681 + if (likely(rc > 0)) {
682 + stream_circular_buffer_del_unsafe(scb, rc);
683 + if (!stats->bytes_outstanding) {
684 + if (!nd_poll_upd(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
685 + nd_log(NDLS_DAEMON, NDLP_ERR,
686 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: cannot update nd_poll()",
687 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
688 +
689 + // recreate the circular buffer if we have to
690 + stream_circular_buffer_recreate_timed_unsafe(rpt->thread.send_to_child.scb, now_ut, false);
691 + stop = true;
692 + }
693 + else if(stream_thread_process_opcodes(sth, &rpt->thread.meta))
694 + stop = true;
695 }
669 - } else if (rc == 0 || errno == ECONNRESET) {
670 - disconnect_reason = "socket reports EOF (closed by child)";
671 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
672 - } else if (rc < 0) {
673 - if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
674 - // will try later
675 - ;
676 - else {
677 - disconnect_reason = "socket reports error while writing";
678 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
696 + else if (rc == 0 || errno == ECONNRESET) {
697 + disconnect_reason = "socket reports EOF (closed by child)";
698 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
699 + }
700 + else if (rc < 0) {
701 + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
702 + // will try later
703 + stop = true;
704 + else {
705 + disconnect_reason = "socket reports error while writing";
706 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
707 + }
708 + }
709 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
710 +
711 + if (disconnect_reason) {
712 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
713 + nd_log(NDLS_DAEMON, NDLP_ERR,
714 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: %s (%zd, on fd %d) - closing connection - "
715 + "we have sent %zu bytes in %zu operations.",
716 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port,
717 + disconnect_reason, rc, rpt->sock.fd, stats->bytes_sent, stats->sends);
718 +
719 + receiver_set_exit_reason(rpt, reason, false);
720 + stream_receiver_remove(sth, rpt, disconnect_reason);
721 + return false;
722 }
723 }
681 - spinlock_unlock(&rpt->thread.send_to_child.spinlock);
682 -
683 - if (disconnect_reason) {
684 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
685 - nd_log(NDLS_DAEMON, NDLP_ERR,
686 - "STREAM RECEIVE[%zu] %s [from %s]: %s (%zd, on fd %d) - closing connection - "
687 - "we have sent %zu bytes in %zu operations.",
688 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, disconnect_reason, rc, rpt->sock.fd,
689 - stats->bytes_sent, stats->sends);
690 -
691 - receiver_set_exit_reason(rpt, reason, false);
692 - stream_receiver_remove(sth, rpt, disconnect_reason);
693 - return;
694 - }
724 + else
725 + break;
726 }
727 }
728
729 if (!(events & ND_POLL_READ))
699 - return;
730 + return true;
731
732 // we can receive data from this socket
733
734 worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
704 - bool removed = false;
705 - while(!removed) {
735 + bool removed = false, stop = false;
736 + size_t iterations = 0;
737 + while(!removed && !stop && iterations++ < MAX_IO_ITERATIONS_PER_EVENT) {
738 ssize_t rc = stream_receive_and_process(sth, rpt, parser, &removed);
739 if (likely(rc > 0)) {
740 rpt->last_msg_t = (time_t)(now_ut / USEC_PER_SEC);
741 +
742 + if(stream_thread_process_opcodes(sth, &rpt->thread.meta))
743 + stop = true;
744 }
745 else if (rc == 0 || errno == ECONNRESET) {
746 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED);
747 nd_log(NDLS_DAEMON, NDLP_ERR,
713 - "STREAM RECEIVE[%zu] %s [from %s]: socket %d reports EOF (closed by child).",
714 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->sock.fd);
748 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: socket %d reports EOF (closed by child).",
749 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
750 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, false);
751 stream_receiver_remove(sth, rpt, "socket reports EOF (closed by child)");
717 - return;
752 + return false;
753 }
754 else if (rc < 0) {
755 if(removed)
721 - return;
756 + return false;
757
758 else if ((errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
759 // will try later
725 - break;
760 + stop = true;
761 else {
762 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
763 nd_log(NDLS_DAEMON, NDLP_ERR,
729 - "STREAM RECEIVE[%zu] %s [from %s]: error during receive (%zd, on fd %d) - closing connection.",
730 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rc, rpt->sock.fd);
764 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: error during receive (%zd, on fd %d) - closing connection.",
765 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rc, rpt->sock.fd);
766 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, false);
767 stream_receiver_remove(sth, rpt, "error during receive");
733 - return;
768 + return false;
769 }
770 }
771 }
772 +
773 + return !removed;
774 }
775
776 void stream_receiver_cleanup(struct stream_thread *sth) {
@@ -782,8 +819,9 @@ bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
819 if (rpt->config.health.delay > 0) {
820 host->health.delay_up_to = now_realtime_sec() + rpt->config.health.delay;
821 nd_log(NDLS_DAEMON, NDLP_DEBUG,
785 - "[%s]: Postponing health checks for %" PRId64 " seconds, because it was just connected.",
786 - rrdhost_hostname(host),
822 + "STREAM RECEIVE '%s' [from [%s]:%s]: "
823 + "Postponing health checks for %" PRId64 " seconds, because it was just connected.",
824 + rrdhost_hostname(host), rpt->client_ip, rpt->client_port,
825 (int64_t) rpt->config.health.delay);
826 }
827 }
@@ -797,7 +835,7 @@ bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
835 signal_rrdcontext = true;
836 stream_receiver_replication_reset(host);
837
800 - rrdhost_flag_clear(rpt->host, RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED);
838 + rrdhost_flag_set(rpt->host, RRDHOST_FLAG_COLLECTOR_ONLINE);
839 aclk_queue_node_info(rpt->host, true);
840
841 rrdhost_stream_parents_reset(host, STREAM_HANDSHAKE_PREPARING);
@@ -810,6 +848,9 @@ bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
848 if(signal_rrdcontext)
849 rrdcontext_host_child_connected(host);
850
851 + if(set_this)
852 + ml_host_start(host);
853 +
854 return set_this;
855 }
856
@@ -822,11 +863,12 @@ void rrdhost_clear_receiver(struct receiver_state *rpt) {
863 // Make sure that we detach this thread and don't kill a freshly arriving receiver
864
865 if (host->receiver == rpt) {
825 - rrdhost_flag_set(host, RRDHOST_FLAG_STREAM_RECEIVER_DISCONNECTED);
866 + rrdhost_flag_clear(host, RRDHOST_FLAG_COLLECTOR_ONLINE);
867 rrdhost_receiver_unlock(host);
868 {
869 // run all these without having the receiver lock
870
871 + ml_host_stop(host);
872 stream_path_child_disconnected(host);
873 stream_sender_signal_to_stop_and_wait(host, STREAM_HANDSHAKE_DISCONNECT_RECEIVER_LEFT, false);
874 stream_receiver_replication_reset(host);
src/streaming/stream-sender-commit.c
+32 -20
@@ -21,14 +21,16 @@ void sender_commit_thread_buffer_free(void) {
21 // Collector thread starting a transmission
22 BUFFER *sender_commit_start_with_trace(struct sender_state *s __maybe_unused, struct sender_buffer *commit, const char *func) {
23 if(unlikely(commit->used))
24 - fatal("STREAMING: thread buffer is used multiple times concurrently (%u). "
24 + fatal("STREAM SEND '%s' [to %s]: thread buffer is used multiple times concurrently (%u). "
25 "It is already being used by '%s()', and now is called by '%s()'",
26 + rrdhost_hostname(s->host), s->connected_to,
27 (unsigned)commit->used,
28 commit->last_function ? commit->last_function : "(null)",
29 func ? func : "(null)");
30
31 if(unlikely(commit->receiver_tid && commit->receiver_tid != gettid_cached()))
31 - fatal("STREAMING: thread buffer is reserved for tid %d, but it used by thread %d function '%s()'.",
32 + fatal("STREAM SEND '%s' [to %s]: thread buffer is reserved for tid %d, but it used by thread %d function '%s()'.",
33 + rrdhost_hostname(s->host), s->connected_to,
34 commit->receiver_tid, gettid_cached(), func ? func : "(null)");
35
36 if(unlikely(commit->wb &&
@@ -81,11 +83,12 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
83 return;
84 }
85
84 - if (unlikely(stream_circular_buffer_set_max_size_unsafe(s->scb, src_len, false))) {
86 + if (unlikely(stream_circular_buffer_set_max_size_unsafe(
87 + s->scb, src_len * STREAM_CIRCULAR_BUFFER_ADAPT_TO_TIMES_MAX_SIZE, false))) {
88 // adaptive sizing of the circular buffer
89 nd_log(NDLS_DAEMON, NDLP_NOTICE,
87 - "STREAM SEND %s [to %s]: Increased max buffer size to %u (message size %zu).",
88 - rrdhost_hostname(s->host), s->connected_to, stats->bytes_max_size, buffer_strlen(wb) + 1);
90 + "STREAM SEND '%s' [to %s]: Increased max buffer size to %u (message size %zu).",
91 + rrdhost_hostname(s->host), s->connected_to, stats->bytes_max_size, src_len + 1);
92 }
93
94 stream_sender_log_payload(s, wb, type, false);
@@ -123,7 +126,7 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
126 size_t dst_len = stream_compress(&s->compressor, src, size_to_compress, &dst);
127 if (!dst_len) {
128 nd_log(NDLS_DAEMON, NDLP_ERR,
126 - "STREAM %s [send to %s]: COMPRESSION failed. Resetting compressor and re-trying",
129 + "STREAM SEND '%s' [to %s]: COMPRESSION failed. Resetting compressor and re-trying",
130 rrdhost_hostname(s->host), s->connected_to);
131
132 stream_compression_initialize(s);
@@ -139,13 +142,16 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
142 size_t decoded_dst_len = stream_decompress_decode_signature((const char *)&signature, sizeof(signature));
143 if (decoded_dst_len != dst_len)
144 fatal(
142 - "STREAM COMPRESSION: invalid signature, original payload %zu bytes, "
145 + "STREAM SEND '%s' [to %s]: invalid signature, original payload %zu bytes, "
146 "compressed payload length %zu bytes, but signature says payload is %zu bytes",
147 + rrdhost_hostname(s->host), s->connected_to,
148 size_to_compress, dst_len, decoded_dst_len);
149 #endif
150
147 - if (!stream_circular_buffer_add_unsafe(s->scb, (const char *)&signature, sizeof(signature), sizeof(signature), type) ||
148 - !stream_circular_buffer_add_unsafe(s->scb, dst, dst_len, size_to_compress, type))
151 + if (!stream_circular_buffer_add_unsafe(s->scb, (const char *)&signature, sizeof(signature),
152 + sizeof(signature), type, false) ||
153 + !stream_circular_buffer_add_unsafe(s->scb, dst, dst_len,
154 + size_to_compress, type, false))
155 goto overflow_with_lock;
156
157 src = src + size_to_compress;
@@ -155,7 +161,8 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
161 else {
162 // uncompressed traffic
163
158 - if (!stream_circular_buffer_add_unsafe(s->scb, src, src_len, src_len, type))
164 + if (!stream_circular_buffer_add_unsafe(s->scb, src, src_len,
165 + src_len, type, false))
166 goto overflow_with_lock;
167 }
168
@@ -179,11 +186,12 @@ overflow_with_lock: {
186 stream_sender_unlock(s);
187 msg.opcode = STREAM_OPCODE_SENDER_BUFFER_OVERFLOW;
188 stream_sender_send_opcode(s, msg);
182 - nd_log(NDLS_DAEMON, NDLP_ERR,
183 - "STREAM %s [send to %s]: buffer overflow (buffer size %u, max size %u, used %u, available %u). "
184 - "Restarting connection.",
185 - rrdhost_hostname(s->host), s->connected_to,
186 - stats->bytes_size, stats->bytes_max_size, stats->bytes_outstanding, stats->bytes_available);
189 + nd_log_limit_static_global_var(erl, 1, 0);
190 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
191 + "STREAM SEND '%s' [to %s]: buffer overflow (buffer size %u, max size %u, used %u, available %u). "
192 + "Restarting connection.",
193 + rrdhost_hostname(s->host), s->connected_to,
194 + stats->bytes_size, stats->bytes_max_size, stats->bytes_outstanding, stats->bytes_available);
195 return;
196 }
197
@@ -193,9 +201,11 @@ compression_failed_with_lock: {
201 stream_sender_unlock(s);
202 msg.opcode = STREAM_OPCODE_SENDER_RECONNECT_WITHOUT_COMPRESSION;
203 stream_sender_send_opcode(s, msg);
196 - nd_log(NDLS_DAEMON, NDLP_ERR,
197 - "STREAM %s [send to %s]: COMPRESSION failed (twice). Deactivating compression and restarting connection.",
198 - rrdhost_hostname(s->host), s->connected_to);
204 + nd_log_limit_static_global_var(erl, 1, 0);
205 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
206 + "STREAM SEND '%s' [to %s]: COMPRESSION failed (twice). "
207 + "Deactivating compression and restarting connection.",
208 + rrdhost_hostname(s->host), s->connected_to);
209 }
210 }
211
@@ -203,10 +213,12 @@ void sender_thread_commit(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYP
213 struct sender_buffer *commit = (wb == commit___thread.wb) ? & commit___thread : &s->host->stream.snd.commit;
214
215 if (unlikely(wb != commit->wb))
206 - fatal("STREAMING: function '%s()' is trying to commit an unknown commit buffer.", func);
216 + fatal("STREAM SEND '%s' [to %s]: function '%s()' is trying to commit an unknown commit buffer.",
217 + rrdhost_hostname(s->host), s->connected_to, func);
218
219 if (unlikely(!commit->used))
209 - fatal("STREAMING: function '%s()' is committing a sender buffer twice.", func);
220 + fatal("STREAM SEND '%s' [to %s]: function '%s()' is committing a sender buffer twice.",
221 + rrdhost_hostname(s->host), s->connected_to, func);
222
223 commit->used = false;
224 commit->last_function = NULL;
src/streaming/stream-sender-execute.c
+8 -5
@@ -26,7 +26,7 @@ static void stream_execute_function_callback(BUFFER *func_wb, int code, void *da
26
27 sender_commit_clean_buffer(s, wb, STREAM_TRAFFIC_TYPE_FUNCTIONS);
28
29 - internal_error(true, "STREAM %s [send to %s] FUNCTION transaction %s sending back response (%zu bytes, %"PRIu64" usec).",
29 + internal_error(true, "STREAM SEND '%s' [to %s]: FUNCTION transaction %s sending back response (%zu bytes, %"PRIu64" usec).",
30 rrdhost_hostname(s->host), s->connected_to,
31 string2str(tmp->transaction),
32 buffer_strlen(func_wb),
@@ -57,7 +57,7 @@ static void execute_commands_function(struct sender_state *s, const char *comman
57 nd_log(NDLS_ACCESS, NDLP_INFO, NULL);
58
59 if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) {
60 - netdata_log_error("STREAM %s [send to %s] %s execution command is incomplete (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
60 + netdata_log_error("STREAM SEND '%s' [to %s]: %s execution command is incomplete (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
61 rrdhost_hostname(s->host), s->connected_to,
62 command,
63 transaction?transaction:"(unset)",
@@ -110,7 +110,10 @@ static void execute_deferred_json(struct sender_state *s, void *data) {
110 if(strcmp(keyword, PLUGINSD_KEYWORD_JSON_CMD_STREAM_PATH) == 0)
111 stream_path_set_from_json(s->host, buffer_tostring(s->defer.payload), true);
112 else
113 - nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM: unknown JSON keyword '%s' with payload: %s", keyword, buffer_tostring(s->defer.payload));
113 + nd_log(NDLS_DAEMON, NDLP_ERR,
114 + "STREAM SEND '%s' [to %s]: unknown JSON keyword '%s' with payload: %s",
115 + rrdhost_hostname(s->host), s->connected_to,
116 + keyword, buffer_tostring(s->defer.payload));
117 }
118
119 static void cleanup_deferred_json(struct sender_state *s __maybe_unused, void *data) {
@@ -274,7 +277,7 @@ void stream_sender_execute_commands(struct sender_state *s) {
277 const char *before = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 4);
278
279 if (!chart_id || !start_streaming || !after || !before) {
277 - netdata_log_error("STREAM %s [send to %s] %s command is incomplete"
280 + netdata_log_error("STREAM SEND '%s' [to %s] %s command is incomplete"
281 " (chart=%s, start_streaming=%s, after=%s, before=%s)",
282 rrdhost_hostname(s->host), s->connected_to,
283 command,
@@ -310,7 +313,7 @@ void stream_sender_execute_commands(struct sender_state *s) {
313 s->defer.action_data = strdupz(keyword);
314 }
315 else {
313 - netdata_log_error("STREAM %s [send to %s] received unknown command over connection: %s",
316 + netdata_log_error("STREAM SEND '%s' [to %s] received unknown command over connection: %s",
317 rrdhost_hostname(s->host), s->connected_to, s->rbuf.line.words[0]?s->rbuf.line.words[0]:"(unset)");
318 }
319
src/streaming/stream-sender-internals.h
+2
@@ -40,6 +40,8 @@ struct sender_state {
40 ND_SOCK sock;
41
42 struct {
43 + bool draining_input; // used exclusively by the stream thread
44 +
45 struct stream_opcode msg; // the template for sending a message to the dispatcher - protected by sender_lock()
46
47 // this is a property of stream_sender_send_msg_to_dispatcher()
src/streaming/stream-sender.c
+96 -75
@@ -89,7 +89,7 @@ void stream_sender_on_connect(struct sender_state *s) {
89
90 static void stream_sender_on_ready_to_dispatch(struct sender_state *s) {
91 nd_log(NDLS_DAEMON, NDLP_DEBUG,
92 - "STREAM SEND [%s]: running ready-to-dispatch hooks...",
92 + "STREAM SEND '%s': running ready-to-dispatch hooks...",
93 rrdhost_hostname(s->host));
94
95 // set this flag before sending any data, or the data will not be sent
@@ -105,7 +105,7 @@ static void stream_sender_on_ready_to_dispatch(struct sender_state *s) {
105
106 static void stream_sender_on_disconnect(struct sender_state *s) {
107 nd_log(NDLS_DAEMON, NDLP_DEBUG,
108 - "STREAM SEND [%s]: running on-disconnect hooks...",
108 + "STREAM SEND '%s': running on-disconnect hooks...",
109 rrdhost_hostname(s->host));
110
111 stream_sender_lock(s);
@@ -182,7 +182,7 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
182 STREAM_CIRCULAR_BUFFER_STATS stats = *stream_circular_buffer_stats_unsafe(s->scb);
183 stream_sender_unlock(s);
184 nd_log(NDLS_DAEMON, NDLP_ERR,
185 - "STREAM SEND[%zu] %s [to %s]: send buffer is full (buffer size %u, max %u, used %u, available %u). "
185 + "STREAM SEND[%zu] '%s' [to %s]: send buffer is full (buffer size %u, max %u, used %u, available %u). "
186 "Restarting connection.",
187 sth->id, rrdhost_hostname(s->host), s->connected_to,
188 stats.bytes_size, stats.bytes_max_size, stats.bytes_outstanding, stats.bytes_available);
@@ -203,7 +203,7 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
203 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_COMPRESSION_ERROR);
204 errno_clear();
205 nd_log(NDLS_DAEMON, NDLP_ERR,
206 - "STREAM SEND[%zu] %s [send to %s]: restarting connection without compression.",
206 + "STREAM SEND[%zu] '%s' [to %s]: restarting connection without compression.",
207 sth->id, rrdhost_hostname(s->host), s->connected_to);
208
209 stream_sender_move_running_to_connector_or_remove(
@@ -245,8 +245,8 @@ void stream_sender_move_queue_to_running_unsafe(struct stream_thread *sth) {
245 ND_LOG_STACK_PUSH(lgs);
246
247 nd_log(NDLS_DAEMON, NDLP_DEBUG,
248 - "STREAM SEND[%zu] [%s]: moving host from dispatcher queue to dispatcher running...",
249 - sth->id, rrdhost_hostname(s->host));
248 + "STREAM SEND[%zu] '%s' [to %s]: moving host from dispatcher queue to dispatcher running...",
249 + sth->id, rrdhost_hostname(s->host), s->connected_to);
250
251 stream_sender_lock(s);
252 s->thread.meta.type = POLLFD_TYPE_SENDER;
@@ -268,7 +268,9 @@ void stream_sender_move_queue_to_running_unsafe(struct stream_thread *sth) {
268 META_SET(&sth->run.meta, (Word_t)&s->thread.meta, &s->thread.meta);
269
270 if(!nd_poll_add(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
271 - nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to add sender socket to nd_poll()");
271 + nd_log(NDLS_DAEMON, NDLP_ERR,
272 + "STREAM SEND[%zu] '%s' [to %s]: failed to add sender socket to nd_poll()",
273 + sth->id, rrdhost_hostname(s->host), s->connected_to);
274
275 stream_sender_on_ready_to_dispatch(s);
276 }
@@ -279,8 +281,8 @@ void stream_sender_remove(struct sender_state *s) {
281 // when it gives up on a certain node
282
283 nd_log(NDLS_DAEMON, NDLP_NOTICE,
282 - "STREAM SEND [%s]: streaming sender removed host: %s",
283 - rrdhost_hostname(s->host), stream_handshake_error_to_string(s->exit.reason));
284 + "STREAM SEND '%s' [to %s]: streaming sender removed host: %s",
285 + rrdhost_hostname(s->host), s->connected_to, stream_handshake_error_to_string(s->exit.reason));
286
287 stream_sender_lock(s);
288
@@ -316,7 +318,9 @@ static void stream_sender_move_running_to_connector_or_remove(struct stream_thre
318 META_DEL(&sth->run.meta, (Word_t)&s->thread.meta);
319
320 if(!nd_poll_del(sth->run.ndpl, s->sock.fd))
319 - nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to delete sender socket from nd_poll()");
321 + nd_log(NDLS_DAEMON, NDLP_ERR,
322 + "STREAM SEND[%zu] '%s' [to %s]: failed to delete sender socket from nd_poll()",
323 + sth->id, rrdhost_hostname(s->host), s->connected_to);
324
325 // clear this flag asap, to stop other threads from pushing metrics for this node
326 rrdhost_flag_clear(s->host, RRDHOST_FLAG_STREAM_SENDER_CONNECTED | RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS);
@@ -331,8 +335,8 @@ static void stream_sender_move_running_to_connector_or_remove(struct stream_thre
335 stream_sender_unlock(s);
336
337 nd_log(NDLS_DAEMON, NDLP_NOTICE,
334 - "STREAM SEND [%s]: sender disconnected from parent, reason: %s",
335 - rrdhost_hostname(s->host), stream_handshake_error_to_string(reason));
338 + "STREAM SEND[%zu] '%s' [to %s]: sender disconnected from parent, reason: %s",
339 + sth->id, rrdhost_hostname(s->host), s->connected_to, stream_handshake_error_to_string(reason));
340
341 nd_sock_close(&s->sock);
342
@@ -398,7 +402,7 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
402 size_snprintf(pending, sizeof(pending), stats.bytes_outstanding, "B", false);
403
404 nd_log(NDLS_DAEMON, NDLP_ERR,
401 - "STREAM SEND[%zu] %s [send to %s]: could not send data for %ld seconds - closing connection - "
405 + "STREAM SEND[%zu] '%s' [to %s]: could not send data for %ld seconds - closing connection - "
406 "we have sent %zu bytes in %zu operations, it is idle for %s, and we have %s pending to send "
407 "(buffer is used %.2f%%).",
408 sth->id, rrdhost_hostname(s->host), s->connected_to, stream_send.parents.timeout_s,
@@ -414,7 +418,7 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
418
419 if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ | (stats.bytes_outstanding ? ND_POLL_WRITE : 0), &s->thread.meta))
420 nd_log(NDLS_DAEMON, NDLP_ERR,
417 - "STREAM SEND[%zu] %s [send to %s]: failed to update nd_poll().",
421 + "STREAM SEND[%zu] '%s' [to %s]: failed to update nd_poll().",
422 sth->id, rrdhost_hostname(s->host), s->connected_to);
423 }
424
@@ -428,7 +432,9 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
432 worker_set_metric(WORKER_SENDER_JOB_BUFFER_RATIO, overall_buffer_ratio);
433 }
434
431 -void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_state *s, nd_poll_event_t events, usec_t now_ut) {
435 +// process poll() events for streaming senders
436 +// returns true when the sender is still there, false if it removed it
437 +bool stream_sender_process_poll_events(struct stream_thread *sth, struct sender_state *s, nd_poll_event_t events, usec_t now_ut) {
438 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
439
440 ND_LOG_STACK lgs[] = {
@@ -464,80 +470,90 @@ void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
470 stream_sender_unlock(s);
471
472 nd_log(NDLS_DAEMON, NDLP_ERR,
467 - "STREAM SEND[%zu] %s [to %s]: %s restarting connection - %zu bytes transmitted in %zu operations.",
473 + "STREAM SEND[%zu] '%s' [to %s]: %s restarting connection - %zu bytes transmitted in %zu operations.",
474 sth->id, rrdhost_hostname(s->host), s->connected_to, error, stats.bytes_sent, stats.sends);
475
476 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, true);
471 - return;
477 + return false;
478 }
479
480 if(events & ND_POLL_WRITE) {
481 // we can send data on this socket
482
477 - if(stream_sender_trylock(s)) {
478 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
479 -
480 - const char *disconnect_reason = NULL;
481 - STREAM_HANDSHAKE reason;
482 -
483 - STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(s->scb);
484 - char *chunk;
485 - size_t outstanding = stream_circular_buffer_get_unsafe(s->scb, &chunk);
486 - ssize_t rc = nd_sock_send_nowait(&s->sock, chunk, outstanding);
487 - if (likely(rc > 0)) {
488 - stream_circular_buffer_del_unsafe(s->scb, rc);
489 - replication_recalculate_buffer_used_ratio_unsafe(s);
490 - s->thread.last_traffic_ut = now_ut;
491 - sth->snd.bytes_sent += rc;
492 -
493 - if (!stats->bytes_outstanding) {
494 - // we sent them all - remove ND_POLL_WRITE
495 - if (!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
496 - nd_log(NDLS_DAEMON, NDLP_ERR,
497 - "STREAM SEND[%zu] %s [send to %s]: failed to update nd_poll().",
498 - sth->id, rrdhost_hostname(s->host), s->connected_to);
499 -
500 - // recreate the circular buffer if we have to
501 - stream_circular_buffer_recreate_timed_unsafe(s->scb, now_ut, false);
483 + bool stop = false;
484 + while(!stop) {
485 + if(stream_sender_trylock(s)) {
486 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
487 +
488 + const char *disconnect_reason = NULL;
489 + STREAM_HANDSHAKE reason;
490 +
491 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(s->scb);
492 + char *chunk;
493 + size_t outstanding = stream_circular_buffer_get_unsafe(s->scb, &chunk);
494 + ssize_t rc = nd_sock_send_nowait(&s->sock, chunk, outstanding);
495 + if (likely(rc > 0)) {
496 + stream_circular_buffer_del_unsafe(s->scb, rc);
497 + replication_recalculate_buffer_used_ratio_unsafe(s);
498 + s->thread.last_traffic_ut = now_ut;
499 + sth->snd.bytes_sent += rc;
500 +
501 + if (!stats->bytes_outstanding) {
502 + // we sent them all - remove ND_POLL_WRITE
503 + if (!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
504 + nd_log(NDLS_DAEMON, NDLP_ERR,
505 + "STREAM SEND[%zu] '%s' [to %s]: failed to update nd_poll().",
506 + sth->id, rrdhost_hostname(s->host), s->connected_to);
507 +
508 + // recreate the circular buffer if we have to
509 + stream_circular_buffer_recreate_timed_unsafe(s->scb, now_ut, false);
510 + stop = true;
511 + }
512 + else if(stream_thread_process_opcodes(sth, &s->thread.meta))
513 + stop = true;
514 }
503 - }
504 - else if (rc == 0 || errno == ECONNRESET) {
505 - disconnect_reason = "socket reports EOF (closed by parent)";
506 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
507 - }
508 - else if (rc < 0) {
509 - if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
510 - // will try later
511 - ;
512 - else {
513 - disconnect_reason = "socket reports error while writing";
514 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
515 + else if (rc == 0 || errno == ECONNRESET) {
516 + disconnect_reason = "socket reports EOF (closed by parent)";
517 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
518 + }
519 + else if (rc < 0) {
520 + if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) {
521 + // will try later
522 + stop = true;
523 + }
524 + else {
525 + disconnect_reason = "socket reports error while writing";
526 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
527 + }
528 + }
529 + stream_sender_unlock(s);
530 +
531 + if (disconnect_reason) {
532 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
533 + nd_log(NDLS_DAEMON, NDLP_ERR,
534 + "STREAM SEND[%zu] '%s' [to %s]: %s (%zd, on fd %d) - restarting connection - "
535 + "we have sent %zu bytes in %zu operations.",
536 + sth->id, rrdhost_hostname(s->host), s->connected_to, disconnect_reason, rc, s->sock.fd,
537 + stats->bytes_sent, stats->sends);
538 +
539 + stream_sender_move_running_to_connector_or_remove(sth, s, reason, true);
540 + return false;
541 }
542 }
517 - stream_sender_unlock(s);
518 -
519 - if (disconnect_reason) {
520 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
521 - nd_log(NDLS_DAEMON, NDLP_ERR,
522 - "STREAM SEND[%zu] %s [to %s]: %s (%zd, on fd %d) - restarting connection - "
523 - "we have sent %zu bytes in %zu operations.",
524 - sth->id, rrdhost_hostname(s->host), s->connected_to, disconnect_reason, rc, s->sock.fd,
525 - stats->bytes_sent, stats->sends);
526 -
527 - stream_sender_move_running_to_connector_or_remove(sth, s, reason, true);
528 -
529 - return;
530 - }
543 + else
544 + break;
545 }
546 }
547
548 if(!(events & ND_POLL_READ))
535 - return;
549 + return true;
550
551 // we can receive data from this socket
552
553 worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
540 - while(true) {
554 + bool stop = false;
555 + size_t iterations = 0;
556 + while(!stop && iterations++ < MAX_IO_ITERATIONS_PER_EVENT) {
557 // we have to drain the socket!
558
559 ssize_t rc = nd_sock_revc_nowait(&s->sock, s->rbuf.b + s->rbuf.read_len, sizeof(s->rbuf.b) - s->rbuf.read_len - 1);
@@ -549,31 +565,36 @@ void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
565
566 worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
567 stream_sender_execute_commands(s);
568 +
569 + if(stream_thread_process_opcodes(sth, &s->thread.meta))
570 + stop = true;
571 }
572 else if (rc == 0 || errno == ECONNRESET) {
573 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED);
574 nd_log(NDLS_DAEMON, NDLP_ERR,
556 - "STREAM SEND[%zu] %s [to %s]: socket %d reports EOF (closed by parent).",
575 + "STREAM SEND[%zu] '%s' [to %s]: socket %d reports EOF (closed by parent).",
576 sth->id, rrdhost_hostname(s->host), s->connected_to, s->sock.fd);
577 stream_sender_move_running_to_connector_or_remove(
578 sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, true);
560 - return;
579 + return false;
580 }
581 else if (rc < 0) {
582 if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
583 // will try later
565 - break;
584 + stop = true;
585 else {
586 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
587 nd_log(NDLS_DAEMON, NDLP_ERR,
569 - "STREAM SEND[%zu] %s [to %s]: error during receive (%zd, on fd %d) - restarting connection.",
588 + "STREAM SEND[%zu] '%s' [to %s]: error during receive (%zd, on fd %d) - restarting connection.",
589 sth->id, rrdhost_hostname(s->host), s->connected_to, rc, s->sock.fd);
590 stream_sender_move_running_to_connector_or_remove(
591 sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, true);
573 - return;
592 + return false;
593 }
594 }
595 }
596 +
597 + return true;
598 }
599
600 void stream_sender_cleanup(struct stream_thread *sth) {
src/streaming/stream-thread.c
+79 -32
@@ -27,8 +27,12 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
27 {
28 if(m->type == POLLFD_TYPE_SENDER) {
29 if(msg->opcode & STREAM_OPCODE_SENDER_POLLOUT) {
30 - if(!nd_poll_upd(sth->run.ndpl, m->s->sock.fd, ND_POLL_READ|ND_POLL_WRITE, m))
31 - internal_fatal(true, "Failed to update sender socket in nd_poll()");
30 + if(!nd_poll_upd(sth->run.ndpl, m->s->sock.fd, ND_POLL_READ|ND_POLL_WRITE, m)) {
31 + nd_log_limit_static_global_var(erl, 1, 0);
32 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_DEBUG,
33 + "STREAM SEND[%zu] '%s' [to %s]: cannot enable output on sender socket %d.",
34 + sth->id, rrdhost_hostname(m->s->host), m->s->connected_to, m->s->sock.fd);
35 + }
36 msg->opcode &= ~(STREAM_OPCODE_SENDER_POLLOUT);
37 }
38
@@ -37,8 +41,12 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
41 }
42 else if(m->type == POLLFD_TYPE_RECEIVER) {
43 if (msg->opcode & STREAM_OPCODE_RECEIVER_POLLOUT) {
40 - if (!nd_poll_upd(sth->run.ndpl, m->rpt->sock.fd, ND_POLL_READ | ND_POLL_WRITE, m))
41 - internal_fatal(true, "Failed to update receiver socket in nd_poll()");
44 + if (!nd_poll_upd(sth->run.ndpl, m->rpt->sock.fd, ND_POLL_READ | ND_POLL_WRITE, m)) {
45 + nd_log_limit_static_global_var(erl, 1, 0);
46 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_DEBUG,
47 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: cannot enable output on receiver socket %d.",
48 + sth->id, rrdhost_hostname(m->rpt->host), m->rpt->client_ip, m->rpt->client_port, m->rpt->sock.fd);
49 + }
50 msg->opcode &= ~(STREAM_OPCODE_RECEIVER_POLLOUT);
51 }
52
@@ -48,7 +56,8 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
56 }
57 else {
58 // this may happen if we receive a POLLOUT opcode, but the sender has been disconnected
51 - nd_log(NDLS_DAEMON, NDLP_DEBUG, "STREAM THREAD[%zu]: OPCODE %u ignored.", sth->id, (unsigned)msg->opcode);
59 + nd_log_limit_static_global_var(erl, 1, 0);
60 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_DEBUG, "STREAM THREAD[%zu]: OPCODE %u ignored.", sth->id, (unsigned)msg->opcode);
61 }
62 }
63
@@ -70,17 +79,22 @@ void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcod
79 if (!msg.session || !msg.meta || !rpt)
80 return;
81
73 - internal_fatal(msg.meta != &rpt->thread.meta, "the receiver pointer in the message does not match this receiver");
82 + if(msg.meta != &rpt->thread.meta) {
83 + nd_log(NDLS_DAEMON, NDLP_ERR,
84 + "STREAM RECEIVE '%s' [from [%s]:%s]: the receiver in the opcode the message does not match this receiver. "
85 + "Ignoring opcode.", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
86 + return;
87 + }
88 struct stream_thread *sth = stream_thread_by_slot_id(msg.thread_slot);
89 if(!sth) {
76 - internal_fatal(true,
77 - "STREAM RECEIVE[x] [%s] thread pointer in the opcode message does not match the expected",
78 - rrdhost_hostname(rpt->host));
90 + nd_log(NDLS_DAEMON, NDLP_ERR,
91 + "STREAM RECEIVE '%s' [from [%s]:%s]: the opcode (%u) message cannot be verified. Ignoring it.",
92 + rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, msg.opcode);
93 return;
94 }
95
96 // check if we can execute the message now
83 - if(msg.opcode == STREAM_OPCODE_RECEIVER_POLLOUT && sth->tid == gettid_cached()) {
97 + if(sth->tid == gettid_cached() && (!rpt->thread.draining_input || msg.opcode == STREAM_OPCODE_RECEIVER_POLLOUT)) {
98 // we are running at the stream thread, and the request is about enabling POLLOUT,
99 // we can do this synchronously.
100 // IMPORTANT: DO NOT HANDLE FAILURES THAT REMOVE THE RECEIVER OR THE SENDER THIS WAY
@@ -108,6 +122,7 @@ void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcod
122 return;
123 }
124
125 +#ifdef NETDATA_INTERNAL_CHECKS
126 // try to find us in the list
127 for (size_t i = 0; i < sth->messages.size; i++) {
128 if (sth->messages.array[i].meta == &rpt->thread.meta) {
@@ -118,8 +133,10 @@ void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcod
133 return;
134 }
135 }
136 +#endif
137
122 - fatal("The streaming opcode queue is full, but this should never happen");
138 + fatal("STREAM RECEIVE '%s' [from [%s]:%s]: The streaming opcode queue is full, but this should never happen...",
139 + rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
140 }
141
142 // let's use a new slot
@@ -142,17 +159,23 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
159 if (!msg.session || !msg.meta || !s)
160 return;
161
145 - internal_fatal(msg.meta != &s->thread.meta, "the sender pointer in the message does not match this sender");
162 + if(msg.meta != &s->thread.meta) {
163 + nd_log(NDLS_DAEMON, NDLP_ERR,
164 + "STREAM SEND '%s' [to %s]: the opcode message does not match this sender. "
165 + "Ignoring opcode.", rrdhost_hostname(s->host), s->connected_to);
166 + return;
167 + }
168 +
169 struct stream_thread *sth = stream_thread_by_slot_id(msg.thread_slot);
170 if(!sth) {
148 - internal_fatal(true,
149 - "STREAM SEND[x] [%s] thread pointer in the opcode message does not match the expected",
150 - rrdhost_hostname(s->host));
171 + nd_log(NDLS_DAEMON, NDLP_ERR,
172 + "STREAM SEND[x] '%s' [to %s] the opcode (%u) message cannot be verified. Ignoring it.",
173 + rrdhost_hostname(s->host), s->connected_to, msg.opcode);
174 return;
175 }
176
177 // check if we can execute the message now
155 - if(msg.opcode == STREAM_OPCODE_SENDER_POLLOUT && sth->tid == gettid_cached()) {
178 + if(sth->tid == gettid_cached() && (!s->thread.draining_input || msg.opcode == STREAM_OPCODE_SENDER_POLLOUT)) {
179 // we are running at the stream thread, and the request is about enabling POLLOUT,
180 // we can do this synchronously.
181 // IMPORTANT: DO NOT HANDLE FAILURES THAT REMOVE THE RECEIVER OR THE SENDER THIS WAY
@@ -180,6 +203,7 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
203 return;
204 }
205
206 +#ifdef NETDATA_INTERNAL_CHECKS
207 // try to find us in the list
208 for (size_t i = 0; i < sth->messages.size; i++) {
209 if (sth->messages.array[i].meta == &s->thread.meta) {
@@ -190,8 +214,10 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
214 return;
215 }
216 }
217 +#endif
218
194 - fatal("the streaming opcode queue is full, but this should never happen");
219 + fatal("STREAM SEND '%s' [to %s]: The streaming opcode queue is full, but this should never happen...",
220 + rrdhost_hostname(s->host), s->connected_to);
221 }
222
223 // let's use a new slot
@@ -210,12 +236,9 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
236 stream_thread_send_pipe_signal(sth);
237 }
238
213 -static void stream_thread_read_pipe_messages(struct stream_thread *sth) {
239 +bool stream_thread_process_opcodes(struct stream_thread *sth, struct pollfd_meta *my_meta) {
240 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
241
216 - if(read(sth->pipe.fds[PIPE_READ], sth->pipe.buffer, sth->pipe.size * sizeof(*sth->pipe.buffer)) <= 0)
217 - nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu]: signal pipe read error", sth->id);
218 -
242 size_t used = 0;
243 spinlock_lock(&sth->messages.spinlock);
244 if(sth->messages.used) {
@@ -225,10 +248,23 @@ static void stream_thread_read_pipe_messages(struct stream_thread *sth) {
248 }
249 spinlock_unlock(&sth->messages.spinlock);
250
251 + bool rc = false;
252 for(size_t i = 0; i < used ;i++) {
253 struct stream_opcode *msg = &sth->messages.copy[i];
254 + if(msg->meta == my_meta) rc = true;
255 stream_thread_handle_op(sth, msg);
256 }
257 +
258 + return rc;
259 +}
260 +
261 +static void stream_thread_read_pipe_messages(struct stream_thread *sth) {
262 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
263 +
264 + if(read(sth->pipe.fds[PIPE_READ], sth->pipe.buffer, sth->pipe.size * sizeof(*sth->pipe.buffer)) <= 0)
265 + nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu]: signal pipe read error", sth->id);
266 +
267 + stream_thread_process_opcodes(sth, NULL);
268 }
269
270 // --------------------------------------------------------------------------------------------------------------------
@@ -265,8 +301,8 @@ static int set_pipe_size(int pipe_fd, int new_size) {
301 static void stream_thread_messages_resize_unsafe(struct stream_thread *sth) {
302 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
303
268 - if(sth->nodes_count >= sth->messages.size) {
269 - size_t new_size = sth->messages.size ? sth->messages.size * 2 : 2;
304 + if(sth->nodes_count * 2 >= sth->messages.size) {
305 + size_t new_size = MAX(sth->messages.size * 2, sth->nodes_count * 2);
306 sth->messages.array = reallocz(sth->messages.array, new_size * sizeof(*sth->messages.array));
307 sth->messages.copy = reallocz(sth->messages.copy, new_size * sizeof(*sth->messages.copy));
308 sth->messages.size = new_size;
@@ -276,20 +312,30 @@ static void stream_thread_messages_resize_unsafe(struct stream_thread *sth) {
312 // --------------------------------------------------------------------------------------------------------------------
313
314 static bool stream_thread_process_poll_slot(struct stream_thread *sth, nd_poll_result_t *ev, usec_t now_ut, size_t *replay_entries) {
315 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
316 +
317 struct pollfd_meta *m = ev->data;
280 - internal_fatal(!m, "Failed to get meta from event");
318 + if(!m) {
319 + nd_log(NDLS_DAEMON, NDLP_ERR,
320 + "STREAM THREAD[%zu]: cannot get meta from nd_poll() event. Ignoring event.", sth->id);
321 + return false;
322 + }
323
324 switch(m->type) {
325 case POLLFD_TYPE_SENDER: {
326 struct sender_state *s = m->s;
285 - stream_sender_process_poll_events(sth, s, ev->events, now_ut);
327 + s->thread.draining_input = true;
328 + if(stream_sender_process_poll_events(sth, s, ev->events, now_ut))
329 + s->thread.draining_input = false;
330 *replay_entries += dictionary_entries(s->replication.requests);
331 break;
332 }
333
334 case POLLFD_TYPE_RECEIVER: {
335 struct receiver_state *rpt = m->rpt;
292 - stream_receive_process_poll_events(sth, rpt, ev->events, now_ut);
336 + rpt->thread.draining_input = true;
337 + if(stream_receive_process_poll_events(sth, rpt, ev->events, now_ut))
338 + rpt->thread.draining_input = false;
339 break;
340 }
341
@@ -427,7 +473,7 @@ void *stream_thread(void *ptr) {
473 META_SET(&sth->run.meta, (Word_t)&sth->run.pipe, &sth->run.pipe);
474
475 if(!nd_poll_add(sth->run.ndpl, sth->pipe.fds[PIPE_READ], ND_POLL_READ, &sth->run.pipe))
430 - internal_fatal(true, "Failed to add pipe to nd_poll()");
476 + nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu]: failed to add pipe to nd_poll()", sth->id);
477
478 bool exit_thread = false;
479 size_t replay_entries = 0;
@@ -484,7 +530,7 @@ void *stream_thread(void *ptr) {
530 internal_fatal(true, "nd_poll() failed");
531 worker_is_busy(WORKER_STREAM_JOB_POLL_ERROR);
532 nd_log_limit_static_thread_var(erl, 1, 1 * USEC_PER_MS);
487 - nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu] poll() returned error", sth->id);
533 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu] nd_poll() returned error", sth->id);
534 continue;
535 }
536
@@ -597,7 +643,8 @@ static struct stream_thread * stream_thread_assign_and_start(RRDHOST *host) {
643 if(!sth->thread) {
644 sth->id = (sth - stream_thread_globals.threads); // find the slot number
645 if(&stream_thread_globals.threads[sth->id] != sth)
600 - fatal("STREAM THREAD[x] [%s]: thread id and slot do not match!", rrdhost_hostname(host));
646 + fatal("STREAM THREAD[x] [%s]: thread and slot owner do not match!",
647 + rrdhost_hostname(host));
648
649 sth->pipe.fds[PIPE_READ] = -1;
650 sth->pipe.fds[PIPE_WRITE] = -1;
@@ -611,7 +658,7 @@ static struct stream_thread * stream_thread_assign_and_start(RRDHOST *host) {
658
659 sth->thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT, stream_thread, sth);
660 if (!sth->thread)
614 - nd_log_daemon(NDLP_ERR, "STREAM THREAD[%zu]: failed to create new thread for client.", sth->id);
661 + nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu]: failed to create new thread for client.", sth->id);
662 }
663
664 spinlock_unlock(&stream_thread_globals.assign.spinlock);
@@ -638,7 +685,7 @@ void stream_receiver_add_to_queue(struct receiver_state *rpt) {
685 stream_thread_node_queued(rpt->host);
686
687 nd_log(NDLS_DAEMON, NDLP_DEBUG,
641 - "STREAM RECEIVE[%zu] [%s]: moving host to receiver queue...",
688 + "STREAM RECEIVE[%zu] '%s': moving host to receiver queue...",
689 sth->id, rrdhost_hostname(rpt->host));
690
691 spinlock_lock(&sth->queue.spinlock);
@@ -653,7 +700,7 @@ void stream_sender_add_to_queue(struct sender_state *s) {
700 stream_thread_node_queued(s->host);
701
702 nd_log(NDLS_DAEMON, NDLP_DEBUG,
656 - "STREAM THREAD[%zu] [%s]: moving host to dispatcher queue...",
703 + "STREAM THREAD[%zu] '%s': moving host to sender queue...",
704 sth->id, rrdhost_hostname(s->host));
705
706 spinlock_lock(&sth->queue.spinlock);
src/streaming/stream-thread.h
+7 -2
@@ -91,6 +91,8 @@ struct stream_opcode {
91 #define STREAM_MAX_THREADS 2048
92 #define THREAD_TAG_STREAM "STREAM"
93
94 +#define MAX_IO_ITERATIONS_PER_EVENT 65536 // drain the input, take it all
95 +
96 typedef enum {
97 POLLFD_TYPE_EMPTY,
98 POLLFD_TYPE_SENDER,
@@ -181,8 +183,8 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
183 void stream_receiver_add_to_queue(struct receiver_state *rpt);
184 void stream_sender_add_to_connector_queue(struct rrdhost *host);
185
184 -void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_state *s, nd_poll_event_t events, usec_t now_ut);
185 -void stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut);
186 +bool stream_sender_process_poll_events(struct stream_thread *sth, struct sender_state *s, nd_poll_event_t events, usec_t now_ut);
187 +bool stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut);
188
189 void stream_sender_cleanup(struct stream_thread *sth);
190 void stream_receiver_cleanup(struct stream_thread *sth);
@@ -193,6 +195,9 @@ struct stream_thread *stream_thread_by_slot_id(size_t thread_slot);
195 void stream_thread_node_queued(struct rrdhost *host);
196 void stream_thread_node_removed(struct rrdhost *host);
197
198 +// returns true if my_meta has received a message
199 +bool stream_thread_process_opcodes(struct stream_thread *sth, struct pollfd_meta *my_meta);
200 +
201 #include "stream-sender-internals.h"
202 #include "stream-receiver-internals.h"
203 #include "plugins.d/pluginsd_parser.h"
src/streaming/stream-traffic-types.h
+8
@@ -3,6 +3,10 @@
3 #ifndef NETDATA_STREAM_TRAFFIC_TYPES_H
4 #define NETDATA_STREAM_TRAFFIC_TYPES_H
5
6 +#ifdef __cplusplus
7 +extern "C" {
8 +#endif
9 +
10 typedef enum __attribute__((packed)) {
11 STREAM_TRAFFIC_TYPE_REPLICATION = 0,
12 STREAM_TRAFFIC_TYPE_FUNCTIONS,
@@ -13,4 +17,8 @@ typedef enum __attribute__((packed)) {
17 STREAM_TRAFFIC_TYPE_MAX,
18 } STREAM_TRAFFIC_TYPE;
19
20 +#ifdef __cplusplus
21 +}
22 +#endif
23 +
24 #endif //NETDATA_STREAM_TRAFFIC_TYPES_H
src/streaming/stream.h
+1
@@ -41,6 +41,7 @@ char *stream_receiver_program_version_strdupz(struct rrdhost *host);
41 #include "rrdhost-status.h"
42 #include "protocol/commands.h"
43 #include "stream-path.h"
44 +#include "stream-control.h"
45
46 void stream_threads_cancel(void);
47
src/web/api/formatters/rrd2json.c
+2
@@ -124,7 +124,9 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
124 wrapper_end = rrdr_json_wrapper_end2;
125 }
126
127 + stream_control_user_data_query_started();
128 RRDR *r = rrd2rrdr(owa, qt);
129 + stream_control_user_data_query_finished();
130
131 if(!r) {
132 buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
src/web/api/queries/query.c
+5 -2
@@ -1964,7 +1964,7 @@ static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_
1964
1965 void store_metric_at_tier(RRDDIM *rd, size_t tier, struct rrddim_tier *t, STORAGE_POINT sp, usec_t now_ut);
1966
1967 -void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s) {
1967 +void backfill_tier_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s) {
1968 if(unlikely(tier >= storage_tiers)) return;
1969 #ifdef ENABLE_DBENGINE
1970 if(default_backfill == RRD_BACKFILL_NONE) return;
@@ -1989,9 +1989,10 @@ void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s
1989 // there is really nothing we can do
1990 if(now_s <= latest_time_s || time_diff < granularity) return;
1991
1992 - struct storage_engine_query_handle seqh;
1992 + stream_control_backfill_query_started();
1993
1994 // for each lower tier
1995 + struct storage_engine_query_handle seqh;
1996 for(int read_tier = (int)tier - 1; read_tier >= 0 ; read_tier--){
1997 time_t smaller_tier_first_time = storage_engine_oldest_time_s(rd->tiers[read_tier].seb, rd->tiers[read_tier].smh);
1998 time_t smaller_tier_last_time = storage_engine_latest_time_s(rd->tiers[read_tier].seb, rd->tiers[read_tier].smh);
@@ -2023,6 +2024,8 @@ void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s
2024 //internal_error(true, "DBENGINE: backfilled chart '%s', dimension '%s', tier %d, from %ld to %ld, with %zu points from tier %d",
2025 // rd->rrdset->name, rd->name, tier, after_wanted, before_wanted, points, tr);
2026 }
2027 +
2028 + stream_control_backfill_query_finished();
2029 }
2030
2031 // ----------------------------------------------------------------------------
src/web/api/queries/query.h
+2
@@ -3,6 +3,8 @@
3 #ifndef NETDATA_API_DATA_QUERY_H
4 #define NETDATA_API_DATA_QUERY_H
5
6 +#include "libnetdata/common.h"
7 +
8 #ifdef __cplusplus
9 extern "C" {
10 #endif
src/web/api/queries/weights.c
+5
@@ -1285,7 +1285,10 @@ NETDATA_DOUBLE *rrd2rrdr_ks2(
1285 };
1286
1287 QUERY_TARGET *qt = query_target_create(&qtr);
1288 + stream_control_user_weights_query_started();
1289 RRDR *r = rrd2rrdr(owa, qt);
1290 + stream_control_user_weights_query_finished();
1291 +
1292 if(!r)
1293 goto cleanup;
1294
@@ -1524,7 +1527,9 @@ static void rrdset_weights_multi_dimensional_value(struct query_weights_data *qw
1527
1528 ONEWAYALLOC *owa = onewayalloc_create(16 * 1024);
1529 QUERY_TARGET *qt = query_target_create(&qtr);
1530 + stream_control_user_weights_query_started();
1531 RRDR *r = rrd2rrdr(owa, qt);
1532 + stream_control_user_weights_query_finished();
1533
1534 if(!r || rrdr_rows(r) != 1 || !r->d || r->d != r->internal.qt->query.used)
1535 goto cleanup;