@cryptotaxi247 / netdata-1 / commits / 9ecf021ec

Streaming improvements #1 (#19137)

* prefer tinysleep over yielding the processor * split spinlocks to separate files * rename spinlock initializers * Optimize ML queuing operations. - Allocate 25% of cores for ML. - Split queues by request type. - Accurate stats for queue operations by type. * abstracted circular buffer into a new private structure to enable using it in receiver sending side - no features added yet, only abstracted the existing functionality - not tested yet * completed the abstraction of stream circular buffer * unified list of receivers and senders; opcodes now support both receivers and senders * use strings in pluginsd * stream receivers send data back to the child using the event loop * do not share pgc aral between caches * pgc uses 4 to 256 partitions, by default equal to the number of CPU cores * add forgotten worker job * workers now monitor spinlock contention * stream sender tries to lock the sender, but does not wait for it - it will be handled later * increase the number of web server threads to the number of cpu cores, with a minimum of 6 * use the nowait versions of nd_sock functions * handle EAGAIN properly * add spinlock contention tracing for rw_spinlock * aral lock/unlock contention tracing * allocate the compressed buffer * use 128KiB for aral default page size; limit memory protection to 5GiB * aral uses mmap() for big pages * enrich log messages * renamed telemetry to pulse * unified sender and receiver socket event loops * logging improvements * NETDATA_LOG_STREAM_SENDER logs inbound and outbound traffic * 16k receiver buffer size to improve interactivity * fix NETDATA_LOG_STREAM_SENDER in sender_execute * do not stream ML models for charts and dimensions that have not been exposed * add support for sending QUIT to plugins and waiting for some time for them to quit gracefully * global spinlock contention per function * use an aral per pgc partition; use 8 partitions for PGD * rrdcalc: do not change the frequency of alerts - it uses arbitrary values used during replication, changing permanently the frequency of alerts replication: use 1/3 of the cores or 1 core every 10 nodes (min of the two) pgd: use as many aral partitions as the CPU cores, up to 256 * aral does 1 allocation per page (the structure and the elements together), instead of two * use the evitor thread only when we run out of memory; restore the optimization about prepending or appending clean pages based on their accesses; use the main cache free memory for the other caches, reducing I/O when the main cache has enough room * reduce the number of events per poll() to 10 * aral allocates pages of up to 1MiB; restore processing 100 events per nd_poll() call * drain the sockets while reading * receiver sockets should be non-blocking * add stability detector to aral * increase the receivers send buffer * do not remove the sender or the receiver while we drain the input sockets --------- Co-authored-by: vkalintiris <vasilis@netdata.cloud>

Costa Tsaousis committed Dec 9, 2024 at 02:37 UTC 9ecf021ec20eb393f769188d89f7abb05439e635
165 files changed +3010 -1939
CMakeLists.txt
+38 -32
@@ -944,6 +944,10 @@ set(LIBNETDATA_FILES
944 src/libnetdata/os/system_memory.h
945 src/libnetdata/socket/nd-poll.c
946 src/libnetdata/socket/nd-poll.h
947 + src/libnetdata/locks/spinlock.c
948 + src/libnetdata/locks/spinlock.h
949 + src/libnetdata/locks/rw-spinlock.c
950 + src/libnetdata/locks/rw-spinlock.h
951 )
952
953 set(LIBH2O_FILES
@@ -1037,8 +1041,8 @@ set(DAEMON_FILES
1041 src/daemon/daemon.h
1042 src/daemon/libuv_workers.c
1043 src/daemon/libuv_workers.h
1040 - src/daemon/telemetry/telemetry.c
1041 - src/daemon/telemetry/telemetry.h
1044 + src/daemon/pulse/pulse.c
1045 + src/daemon/pulse/pulse.h
1046 src/daemon/analytics.c
1047 src/daemon/analytics.h
1048 src/daemon/main.c
@@ -1068,36 +1072,36 @@ set(DAEMON_FILES
1072 src/daemon/dyncfg/dyncfg-internals.h
1073 src/daemon/dyncfg/dyncfg-intercept.c
1074 src/daemon/dyncfg/dyncfg-tree.c
1071 - src/daemon/telemetry/telemetry-http-api.c
1072 - src/daemon/telemetry/telemetry-http-api.h
1073 - src/daemon/telemetry/telemetry-queries.c
1074 - src/daemon/telemetry/telemetry-queries.h
1075 - src/daemon/telemetry/telemetry-ingestion.c
1076 - src/daemon/telemetry/telemetry-ingestion.h
1077 - src/daemon/telemetry/telemetry-ml.c
1078 - src/daemon/telemetry/telemetry-ml.h
1079 - src/daemon/telemetry/telemetry-gorilla.c
1080 - src/daemon/telemetry/telemetry-gorilla.h
1081 - src/daemon/telemetry/telemetry-daemon.c
1082 - src/daemon/telemetry/telemetry-daemon.h
1083 - src/daemon/telemetry/telemetry-daemon-memory.c
1084 - src/daemon/telemetry/telemetry-daemon-memory.h
1085 - src/daemon/telemetry/telemetry-sqlite3.c
1086 - src/daemon/telemetry/telemetry-sqlite3.h
1087 - src/daemon/telemetry/telemetry-dbengine.c
1088 - src/daemon/telemetry/telemetry-dbengine.h
1089 - src/daemon/telemetry/telemetry-string.c
1090 - src/daemon/telemetry/telemetry-string.h
1091 - src/daemon/telemetry/telemetry-heartbeat.c
1092 - src/daemon/telemetry/telemetry-heartbeat.h
1093 - src/daemon/telemetry/telemetry-dictionary.c
1094 - src/daemon/telemetry/telemetry-dictionary.h
1095 - src/daemon/telemetry/telemetry-workers.c
1096 - src/daemon/telemetry/telemetry-workers.h
1097 - src/daemon/telemetry/telemetry-trace-allocations.c
1098 - src/daemon/telemetry/telemetry-trace-allocations.h
1099 - src/daemon/telemetry/telemetry-aral.c
1100 - src/daemon/telemetry/telemetry-aral.h
1075 + src/daemon/pulse/pulse-http-api.c
1076 + src/daemon/pulse/pulse-http-api.h
1077 + src/daemon/pulse/pulse-queries.c
1078 + src/daemon/pulse/pulse-queries.h
1079 + src/daemon/pulse/pulse-ingestion.c
1080 + src/daemon/pulse/pulse-ingestion.h
1081 + src/daemon/pulse/pulse-ml.c
1082 + src/daemon/pulse/pulse-ml.h
1083 + src/daemon/pulse/pulse-gorilla.c
1084 + src/daemon/pulse/pulse-gorilla.h
1085 + src/daemon/pulse/pulse-daemon.c
1086 + src/daemon/pulse/pulse-daemon.h
1087 + src/daemon/pulse/pulse-daemon-memory.c
1088 + src/daemon/pulse/pulse-daemon-memory.h
1089 + src/daemon/pulse/pulse-sqlite3.c
1090 + src/daemon/pulse/pulse-sqlite3.h
1091 + src/daemon/pulse/pulse-dbengine.c
1092 + src/daemon/pulse/pulse-dbengine.h
1093 + src/daemon/pulse/pulse-string.c
1094 + src/daemon/pulse/pulse-string.h
1095 + src/daemon/pulse/pulse-heartbeat.c
1096 + src/daemon/pulse/pulse-heartbeat.h
1097 + src/daemon/pulse/pulse-dictionary.c
1098 + src/daemon/pulse/pulse-dictionary.h
1099 + src/daemon/pulse/pulse-workers.c
1100 + src/daemon/pulse/pulse-workers.h
1101 + src/daemon/pulse/pulse-trace-allocations.c
1102 + src/daemon/pulse/pulse-trace-allocations.h
1103 + src/daemon/pulse/pulse-aral.c
1104 + src/daemon/pulse/pulse-aral.h
1105 src/daemon/config/netdata-conf-db.c
1106 src/daemon/config/netdata-conf-db.h
1107 src/daemon/config/netdata-conf.h
@@ -1543,6 +1547,8 @@ set(STREAMING_PLUGIN_FILES
1547 src/streaming/stream-receiver-connection.c
1548 src/streaming/stream-sender-commit.h
1549 src/streaming/stream-traffic-types.h
1550 + src/streaming/stream-circular-buffer.c
1551 + src/streaming/stream-circular-buffer.h
1552 )
1553
1554 set(WEB_PLUGIN_FILES
src/aclk/aclk_query.c
+1 -1
@@ -16,7 +16,7 @@ struct pending_req_list {
16 };
17
18 static struct pending_req_list *pending_req_list_head = NULL;
19 -static SPINLOCK pending_req_list_lock = NETDATA_SPINLOCK_INITIALIZER;
19 +static SPINLOCK pending_req_list_lock = SPINLOCK_INITIALIZER;
20
21 void aclk_config_get_query_scope(void) {
22 const char *s = config_get(CONFIG_SECTION_CLOUD, "scope", "full");
src/aclk/https_client.c
+1 -1
@@ -6,7 +6,7 @@
6
7 #include "aclk_util.h"
8
9 -#include "daemon/telemetry/telemetry.h"
9 +#include "daemon/pulse/pulse.h"
10
11 static const char *http_req_type_to_str(http_req_type_t req) {
12 switch (req) {
src/claim/claim-with-api.c
+2 -2
@@ -361,7 +361,7 @@ static bool send_curl_request(const char *machine_guid, const char *hostname, co
361 }
362
363 bool claim_agent(const char *url, const char *token, const char *rooms, const char *proxy, bool insecure) {
364 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
364 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
365 spinlock_lock(&spinlock);
366
367 if (!check_and_generate_certificates()) {
@@ -411,7 +411,7 @@ bool claim_agent_from_environment(void) {
411
412 bool claim_agent_from_claim_conf(void) {
413 static struct config claim_config = APPCONFIG_INITIALIZER;
414 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
414 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
415 bool ret = false;
416
417 spinlock_lock(&spinlock);
src/claim/claim_id.c
+1 -1
@@ -7,7 +7,7 @@ static struct {
7 ND_UUID claim_uuid;
8 ND_UUID claim_uuid_saved;
9 } claim = {
10 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
10 + .spinlock = SPINLOCK_INITIALIZER,
11 };
12
13 void claim_id_clear_previous_working(void) {
src/collectors/cgroups.plugin/cgroup-network.c
+1 -1
@@ -554,7 +554,7 @@ static void read_from_spawned(SPAWN_INSTANCE *si, const char *name __maybe_unuse
554 }
555 fclose(fp);
556 spawn_server_instance_read_fd_unset(si);
557 - spawn_server_exec_kill(spawn_server, si);
557 + spawn_server_exec_kill(spawn_server, si, 0);
558 }
559
560 void detect_veth_interfaces_spawn(pid_t pid) {
src/collectors/ebpf.plugin/ebpf_apps.c
+1 -1
@@ -307,7 +307,7 @@ int ebpf_read_apps_groups_conf(struct ebpf_target **agdt, struct ebpf_target **a
307 #define MAX_CMDLINE 16384
308
309 Pvoid_t ebpf_pid_judyL = NULL;
310 -SPINLOCK ebpf_pid_spinlock = NETDATA_SPINLOCK_INITIALIZER;
310 +SPINLOCK ebpf_pid_spinlock = SPINLOCK_INITIALIZER;
311
312 void ebpf_pid_del(pid_t pid)
313 {
src/collectors/freeipmi.plugin/freeipmi_plugin.c
+2 -2
@@ -1959,7 +1959,7 @@ int main (int argc, char **argv) {
1959 struct ipmi_collection_thread sensors_data = {
1960 .type = IPMI_COLLECT_TYPE_SENSORS,
1961 .freq_s = update_every,
1962 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
1962 + .spinlock = SPINLOCK_INITIALIZER,
1963 .debug = debug,
1964 .state = {
1965 .debug = debug,
@@ -1974,7 +1974,7 @@ int main (int argc, char **argv) {
1974 }, sel_data = {
1975 .type = IPMI_COLLECT_TYPE_SEL,
1976 .freq_s = update_every_sel,
1977 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
1977 + .spinlock = SPINLOCK_INITIALIZER,
1978 .debug = debug,
1979 .state = {
1980 .debug = debug,
src/collectors/proc.plugin/proc_net_dev_renames.c
+1 -1
@@ -15,7 +15,7 @@ static void dictionary_netdev_rename_delete_cb(const DICTIONARY_ITEM *item __may
15 }
16
17 void netdev_renames_init(void) {
18 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
18 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
19
20 spinlock_lock(&spinlock);
21 if(!netdev_renames) {
src/collectors/statsd.plugin/statsd.c
+2 -2
@@ -2654,7 +2654,7 @@ void *statsd_main(void *ptr) {
2654 RRDSET *st_pcharts = NULL;
2655 RRDDIM *rd_pcharts = NULL;
2656
2657 - if(telemetry_enabled) {
2657 + if(pulse_enabled) {
2658 st_metrics = rrdset_create_localhost(
2659 "netdata",
2660 "statsd_metrics",
@@ -2851,7 +2851,7 @@ void *statsd_main(void *ptr) {
2851 if(unlikely(!service_running(SERVICE_COLLECTORS)))
2852 break;
2853
2854 - if(telemetry_enabled) {
2854 + if(pulse_enabled) {
2855 rrddim_set_by_pointer(st_metrics, rd_metrics_gauge, (collected_number)statsd.gauges.metrics);
2856 rrddim_set_by_pointer(st_metrics, rd_metrics_counter, (collected_number)statsd.counters.metrics);
2857 rrddim_set_by_pointer(st_metrics, rd_metrics_timer, (collected_number)statsd.timers.metrics);
src/collectors/systemd-journal.plugin/systemd-journal-files.c
+1 -1
@@ -704,7 +704,7 @@ int filenames_compar(const void *a, const void *b) {
704 }
705
706 void journal_files_registry_update(void) {
707 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
707 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
708
709 if(spinlock_trylock(&spinlock)) {
710 usec_t scan_monotonic_ut = now_monotonic_usec();
src/collectors/tc.plugin/plugin_tc.c
+1 -1
@@ -1135,7 +1135,7 @@ void *tc_main(void *ptr) {
1135 }
1136
1137 // fgets() failed or loop broke
1138 - int code = spawn_popen_kill(tc_child_instance);
1138 + int code = spawn_popen_kill(tc_child_instance, 0);
1139 tc_child_instance = NULL;
1140
1141 if(unlikely(device)) {
src/collectors/windows-events.plugin/windows-events-providers.c
+1 -1
@@ -75,7 +75,7 @@ static struct {
75 ARAL *aral_providers;
76 ARAL *aral_handles;
77 } pbc = {
78 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
78 + .spinlock = SPINLOCK_INITIALIZER,
79 };
80
81 static void provider_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property,
src/collectors/windows-events.plugin/windows-events-sources.c
+1 -1
@@ -484,7 +484,7 @@ cleanup:
484 }
485
486 void wevt_sources_scan(void) {
487 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
487 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
488 LPWSTR channel = NULL;
489 EVT_HANDLE hChannelEnum = NULL;
490
src/daemon/buildinfo.c
+1 -1
@@ -1263,7 +1263,7 @@ __attribute__((constructor)) void initialize_build_info(void) {
1263 int get_system_info(struct rrdhost_system_info *system_info);
1264 static void populate_system_info(void) {
1265 static bool populated = false;
1266 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
1266 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
1267
1268 if(populated)
1269 return;
src/daemon/common.h
+1 -1
@@ -19,7 +19,7 @@ extern "C" {
19 #include "daemon/config/netdata-conf.h"
20 #include "daemon/dyncfg/dyncfg.h"
21
22 -#include "daemon/telemetry/telemetry.h"
22 +#include "daemon/pulse/pulse.h"
23
24 // health monitoring and alarm notifications
25 #include "health/health.h"
src/daemon/config/netdata-conf-backwards-compatibility.c
+16 -4
@@ -181,14 +181,26 @@ void netdata_conf_backwards_compatibility(void) {
181 config_move(CONFIG_SECTION_GLOBAL, "enable zero metrics",
182 CONFIG_SECTION_DB, "enable zero metrics");
183
184 - config_move("global statistics", "update every",
185 - CONFIG_SECTION_TELEMETRY, "update every");
184 + // ----------------------------------------------------------------------------------------------------------------
185 + // global statistics -> telemetry -> pulse
186
187 config_move(CONFIG_SECTION_PLUGINS, "netdata monitoring",
188 - CONFIG_SECTION_PLUGINS, "netdata telemetry");
188 + CONFIG_SECTION_PLUGINS, "netdata pulse");
189 +
190 + config_move(CONFIG_SECTION_PLUGINS, "netdata telemetry",
191 + CONFIG_SECTION_PLUGINS, "netdata pulse");
192
193 config_move(CONFIG_SECTION_PLUGINS, "netdata monitoring extended",
191 - CONFIG_SECTION_TELEMETRY, "extended telemetry");
194 + CONFIG_SECTION_PULSE, "extended");
195 +
196 + config_move("telemetry", "extended telemetry",
197 + CONFIG_SECTION_PULSE, "extended");
198 +
199 + config_move("global statistics", "update every",
200 + CONFIG_SECTION_PULSE, "update every");
201 +
202 + config_move("telemetry", "update every",
203 + CONFIG_SECTION_PULSE, "update every");
204
205
206 // ----------------------------------------------------------------------------------------------------------------
src/daemon/config/netdata-conf-db.c
+4 -1
@@ -145,8 +145,11 @@ void netdata_conf_dbengine_init(const char *hostname) {
145 OS_SYSTEM_MEMORY sm = os_system_memory(true);
146 if(sm.ram_total_bytes && sm.ram_available_bytes && sm.ram_total_bytes > sm.ram_available_bytes) {
147 // calculate the default out of memory protection size
148 + uint64_t keep_free = sm.ram_total_bytes / 10;
149 + if(keep_free > 5ULL * 1024 * 1024 * 1024)
150 + keep_free = 5ULL * 1024 * 1024 * 1024;
151 char buf[64];
149 - size_snprintf(buf, sizeof(buf), sm.ram_total_bytes / 10, "B", false);
152 + size_snprintf(buf, sizeof(buf), keep_free, "B", false);
153 size_parse(buf, &dbengine_out_of_memory_protection, "B");
154 }
155
src/daemon/main.c
+5 -5
@@ -1424,14 +1424,14 @@ int netdata_main(int argc, char **argv) {
1424 default_stacksize = 1 * 1024 * 1024;
1425
1426 #ifdef NETDATA_INTERNAL_CHECKS
1427 - telemetry_enabled = true;
1428 - telemetry_extended_enabled = true;
1427 + pulse_enabled = true;
1428 + pulse_extended_enabled = true;
1429 #endif
1430
1431 - telemetry_extended_enabled =
1432 - config_get_boolean(CONFIG_SECTION_TELEMETRY, "extended telemetry", telemetry_extended_enabled);
1431 + pulse_extended_enabled =
1432 + config_get_boolean(CONFIG_SECTION_PULSE, "extended", pulse_extended_enabled);
1433
1434 - if(telemetry_extended_enabled)
1434 + if(pulse_extended_enabled)
1435 // this has to run before starting any other threads that use workers
1436 workers_utilization_enable();
1437
src/daemon/pulse/pulse-aral.c renamed
+11 -11
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-aral.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-aral.h"
5
6 struct aral_info {
7 const char *name;
@@ -19,7 +19,7 @@ static struct {
19 ARAL_STATS_JudyLSet idx;
20 } globals = { 0 };
21
22 -static void telemetry_aral_register_statistics(struct aral_statistics *stats, const char *name) {
22 +static void pulse_aral_register_statistics(struct aral_statistics *stats, const char *name) {
23 if(!name || !stats)
24 return;
25
@@ -33,7 +33,7 @@ static void telemetry_aral_register_statistics(struct aral_statistics *stats, co
33 spinlock_unlock(&globals.spinlock);
34 }
35
36 -void telemetry_aral_register(ARAL *ar, const char *name) {
36 +void pulse_aral_register(ARAL *ar, const char *name) {
37 if(!ar) return;
38
39 if(!name)
@@ -41,10 +41,10 @@ void telemetry_aral_register(ARAL *ar, const char *name) {
41
42 struct aral_statistics *stats = aral_get_statistics(ar);
43
44 - telemetry_aral_register_statistics(stats, name);
44 + pulse_aral_register_statistics(stats, name);
45 }
46
47 -void telemetry_aral_unregister(ARAL *ar) {
47 +void pulse_aral_unregister(ARAL *ar) {
48 if(!ar) return;
49 struct aral_statistics *stats = aral_get_statistics(ar);
50
@@ -58,11 +58,11 @@ void telemetry_aral_unregister(ARAL *ar) {
58 spinlock_unlock(&globals.spinlock);
59 }
60
61 -void telemerty_aral_init(void) {
62 - telemetry_aral_register_statistics(aral_by_size_statistics(), "by-size");
61 +void pulse_aral_init(void) {
62 + pulse_aral_register_statistics(aral_by_size_statistics(), "by-size");
63 }
64
65 -void telemetry_aral_do(bool extended) {
65 +void pulse_aral_do(bool extended) {
66 if(!extended) return;
67
68 spinlock_lock(&globals.spinlock);
@@ -111,7 +111,7 @@ void telemetry_aral_do(bool extended) {
111 "Array Allocator Memory Utilization",
112 "bytes",
113 "netdata",
114 - "telemetry",
114 + "pulse",
115 910000,
116 localhost->rrd_update_every,
117 RRDSET_TYPE_STACKED);
@@ -145,7 +145,7 @@ void telemetry_aral_do(bool extended) {
145 "Array Allocator Memory Utilization",
146 "%",
147 "netdata",
148 - "telemetry",
148 + "pulse",
149 910001,
150 localhost->rrd_update_every,
151 RRDSET_TYPE_LINE);
src/daemon/pulse/pulse-aral.h new
+16
@@ -0,0 +1,16 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_ARAL_H
4 +#define NETDATA_PULSE_ARAL_H
5 +
6 +#include "daemon/common.h"
7 +
8 +void pulse_aral_register(ARAL *ar, const char *name);
9 +void pulse_aral_unregister(ARAL *ar);
10 +
11 +#if defined(PULSE_INTERNALS)
12 +void pulse_aral_init(void);
13 +void pulse_aral_do(bool extended);
14 +#endif
15 +
16 +#endif //NETDATA_PULSE_ARAL_H
src/daemon/pulse/pulse-daemon-memory.c renamed
+8 -7
@@ -1,14 +1,15 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-daemon-memory.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-daemon-memory.h"
5 +#include "streaming/replication.h"
6
7 #define dictionary_stats_memory_total(stats) \
8 ((stats).memory.dict + (stats).memory.values + (stats).memory.index)
9
10 struct netdata_buffers_statistics netdata_buffers_statistics = {};
11
11 -void telemetry_daemon_memory_do(bool extended) {
12 +void pulse_daemon_memory_do(bool extended) {
13 {
14 static RRDSET *st_memory = NULL;
15 static RRDDIM *rd_database = NULL;
@@ -44,7 +45,7 @@ void telemetry_daemon_memory_do(bool extended) {
45 "Netdata Memory",
46 "bytes",
47 "netdata",
47 - "stats",
48 + "pulse",
49 130100,
50 localhost->rrd_update_every,
51 RRDSET_TYPE_STACKED);
@@ -94,7 +95,7 @@ void telemetry_daemon_memory_do(bool extended) {
95 string_statistics(NULL, NULL, NULL, NULL, NULL, &strings, NULL, NULL);
96
97 rrddim_set_by_pointer(st_memory, rd_database,
97 - (collected_number)telemetry_dbengine_total_memory + (collected_number)rrddim_db_memory_size);
98 + (collected_number)pulse_dbengine_total_memory + (collected_number)rrddim_db_memory_size);
99
100 #ifdef DICT_WITH_STATS
101 rrddim_set_by_pointer(st_memory, rd_collectors,
@@ -141,7 +142,7 @@ void telemetry_daemon_memory_do(bool extended) {
142 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_rrdlabels));
143
144 rrddim_set_by_pointer(st_memory, rd_ml,
144 - (collected_number)telemetry_ml_get_current_memory_usage());
145 + (collected_number)pulse_ml_get_current_memory_usage());
146
147 rrddim_set_by_pointer(st_memory, rd_strings,
148 (collected_number)strings);
@@ -194,7 +195,7 @@ void telemetry_daemon_memory_do(bool extended) {
195 "Netdata Memory Buffers",
196 "bytes",
197 "netdata",
197 - "stats",
198 + "pulse",
199 130101,
200 localhost->rrd_update_every,
201 RRDSET_TYPE_STACKED);
src/daemon/pulse/pulse-daemon-memory.h renamed
+5 -5
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#ifndef NETDATA_TELEMETRY_DAEMON_MEMORY_H
4 -#define NETDATA_TELEMETRY_DAEMON_MEMORY_H
3 +#ifndef NETDATA_PULSE_DAEMON_MEMORY_H
4 +#define NETDATA_PULSE_DAEMON_MEMORY_H
5
6 #include "daemon/common.h"
7
@@ -22,8 +22,8 @@ extern struct netdata_buffers_statistics {
22 size_t buffers_web;
23 } netdata_buffers_statistics;
24
25 -#if defined(TELEMETRY_INTERNALS)
26 -void telemetry_daemon_memory_do(bool extended);
25 +#if defined(PULSE_INTERNALS)
26 +void pulse_daemon_memory_do(bool extended);
27 #endif
28
29 -#endif //NETDATA_TELEMETRY_DAEMON_MEMORY_H
29 +#endif //NETDATA_PULSE_DAEMON_MEMORY_H
src/daemon/pulse/pulse-daemon.c renamed
+10 -10
@@ -1,9 +1,9 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-daemon.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-daemon.h"
5
6 -static void telemetry_daemon_cpu_usage_do(bool extended __maybe_unused) {
6 +static void pulse_daemon_cpu_usage_do(bool extended __maybe_unused) {
7 struct rusage me;
8 getrusage(RUSAGE_SELF, &me);
9
@@ -22,7 +22,7 @@ static void telemetry_daemon_cpu_usage_do(bool extended __maybe_unused) {
22 , "Netdata CPU usage"
23 , "milliseconds/s"
24 , "netdata"
25 - , "stats"
25 + , "pulse"
26 , 130000
27 , localhost->rrd_update_every
28 , RRDSET_TYPE_STACKED
@@ -38,7 +38,7 @@ static void telemetry_daemon_cpu_usage_do(bool extended __maybe_unused) {
38 }
39 }
40
41 -static void telemetry_daemon_uptime_do(bool extended __maybe_unused) {
41 +static void pulse_daemon_uptime_do(bool extended __maybe_unused) {
42 {
43 static time_t netdata_boottime_time = 0;
44 if (!netdata_boottime_time)
@@ -59,7 +59,7 @@ static void telemetry_daemon_uptime_do(bool extended __maybe_unused) {
59 "Netdata uptime",
60 "seconds",
61 "netdata",
62 - "stats",
62 + "pulse",
63 130150,
64 localhost->rrd_update_every,
65 RRDSET_TYPE_LINE);
@@ -72,8 +72,8 @@ static void telemetry_daemon_uptime_do(bool extended __maybe_unused) {
72 }
73 }
74
75 -void telemetry_daemon_do(bool extended) {
76 - telemetry_daemon_cpu_usage_do(extended);
77 - telemetry_daemon_uptime_do(extended);
78 - telemetry_daemon_memory_do(extended);
75 +void pulse_daemon_do(bool extended) {
76 + pulse_daemon_cpu_usage_do(extended);
77 + pulse_daemon_uptime_do(extended);
78 + pulse_daemon_memory_do(extended);
79 }
src/daemon/pulse/pulse-daemon.h new
+12
@@ -0,0 +1,12 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_DAEMON_H
4 +#define NETDATA_PULSE_DAEMON_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#if defined(PULSE_INTERNALS)
9 +void pulse_daemon_do(bool extended);
10 +#endif
11 +
12 +#endif //NETDATA_PULSE_DAEMON_H
src/daemon/pulse/pulse-dbengine.c renamed
+39 -39
@@ -1,9 +1,9 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-dbengine.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-dbengine.h"
5
6 -size_t telemetry_dbengine_total_memory = 0;
6 +size_t pulse_dbengine_total_memory = 0;
7
8 #if defined(ENABLE_DBENGINE)
9
@@ -111,7 +111,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
111 buffer_tostring(title),
112 "%",
113 "netdata",
114 - "stats",
114 + "pulse",
115 priority,
116 localhost->rrd_update_every,
117 RRDSET_TYPE_LINE);
@@ -159,7 +159,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
159 buffer_tostring(title),
160 "ops/s",
161 "netdata",
162 - "stats",
162 + "pulse",
163 priority,
164 localhost->rrd_update_every,
165 RRDSET_TYPE_LINE);
@@ -213,7 +213,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
213 buffer_tostring(title),
214 "bytes",
215 "netdata",
216 - "stats",
216 + "pulse",
217 priority,
218 localhost->rrd_update_every,
219 RRDSET_TYPE_STACKED);
@@ -285,7 +285,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
285 buffer_tostring(title),
286 "pages",
287 "netdata",
288 - "stats",
288 + "pulse",
289 priority,
290 localhost->rrd_update_every,
291 RRDSET_TYPE_HEATMAP);
@@ -331,7 +331,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
331 buffer_tostring(title),
332 "bytes",
333 "netdata",
334 - "stats",
334 + "pulse",
335 priority,
336 localhost->rrd_update_every,
337 RRDSET_TYPE_LINE);
@@ -381,7 +381,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
381 buffer_tostring(title),
382 "pages",
383 "netdata",
384 - "stats",
384 + "pulse",
385 priority,
386 localhost->rrd_update_every,
387 RRDSET_TYPE_LINE);
@@ -425,7 +425,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
425 buffer_tostring(title),
426 "bytes/s",
427 "netdata",
428 - "stats",
428 + "pulse",
429 priority,
430 localhost->rrd_update_every,
431 RRDSET_TYPE_AREA);
@@ -467,7 +467,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
467 buffer_tostring(title),
468 "bytes/s",
469 "netdata",
470 - "stats",
470 + "pulse",
471 priority,
472 localhost->rrd_update_every,
473 RRDSET_TYPE_AREA);
@@ -507,7 +507,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
507 buffer_tostring(title),
508 "events/s",
509 "netdata",
510 - "stats",
510 + "pulse",
511 priority,
512 localhost->rrd_update_every,
513 RRDSET_TYPE_AREA);
@@ -549,7 +549,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
549 buffer_tostring(title),
550 "events/s",
551 "netdata",
552 - "stats",
552 + "pulse",
553 priority,
554 localhost->rrd_update_every,
555 RRDSET_TYPE_LINE);
@@ -609,7 +609,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
609 buffer_tostring(title),
610 "workers",
611 "netdata",
612 - "stats",
612 + "pulse",
613 priority,
614 localhost->rrd_update_every,
615 RRDSET_TYPE_LINE);
@@ -639,7 +639,7 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
639 }
640
641
642 -void telemetry_dbengine_do(bool extended) {
642 +void pulse_dbengine_do(bool extended) {
643 if(!main_cache || !main_mrg || !extended)
644 return;
645
@@ -676,7 +676,7 @@ void telemetry_dbengine_do(bool extended) {
676 buffers_total_size += buffers.julyl;
677 #endif
678
679 - telemetry_dbengine_total_memory = pgc_main_stats.size + pgc_open_stats.size + pgc_extent_stats.size + mrg_stats.size + buffers_total_size;
679 + pulse_dbengine_total_memory = pgc_main_stats.size + pgc_open_stats.size + pgc_extent_stats.size + mrg_stats.size + buffers_total_size;
680
681 size_t priority = 135000;
682
@@ -698,7 +698,7 @@ void telemetry_dbengine_do(bool extended) {
698 "Netdata DB Memory",
699 "bytes",
700 "netdata",
701 - "stats",
701 + "pulse",
702 priority,
703 localhost->rrd_update_every,
704 RRDSET_TYPE_STACKED);
@@ -751,7 +751,7 @@ void telemetry_dbengine_do(bool extended) {
751 "Netdata DB Buffers",
752 "bytes",
753 "netdata",
754 - "stats",
754 + "pulse",
755 priority,
756 localhost->rrd_update_every,
757 RRDSET_TYPE_STACKED);
@@ -812,7 +812,7 @@ void telemetry_dbengine_do(bool extended) {
812 "Netdata JulyL Memory Moved",
813 "bytes/s",
814 "netdata",
815 - "stats",
815 + "pulse",
816 priority,
817 localhost->rrd_update_every,
818 RRDSET_TYPE_AREA);
@@ -844,7 +844,7 @@ void telemetry_dbengine_do(bool extended) {
844 "Netdata Metrics in Metrics Registry",
845 "metrics",
846 "netdata",
847 - "stats",
847 + "pulse",
848 priority,
849 localhost->rrd_update_every,
850 RRDSET_TYPE_LINE);
@@ -880,7 +880,7 @@ void telemetry_dbengine_do(bool extended) {
880 "Netdata Metrics Registry Operations",
881 "metrics",
882 "netdata",
883 - "stats",
883 + "pulse",
884 priority,
885 localhost->rrd_update_every,
886 RRDSET_TYPE_LINE);
@@ -912,7 +912,7 @@ void telemetry_dbengine_do(bool extended) {
912 "Netdata Metrics Registry References",
913 "references",
914 "netdata",
915 - "stats",
915 + "pulse",
916 priority,
917 localhost->rrd_update_every,
918 RRDSET_TYPE_LINE);
@@ -943,7 +943,7 @@ void telemetry_dbengine_do(bool extended) {
943 "Netdata Queries Cache Hit Ratio",
944 "%",
945 "netdata",
946 - "stats",
946 + "pulse",
947 priority,
948 localhost->rrd_update_every,
949 RRDSET_TYPE_LINE);
@@ -1020,7 +1020,7 @@ void telemetry_dbengine_do(bool extended) {
1020 "Netdata Queries",
1021 "queries/s",
1022 "netdata",
1023 - "stats",
1023 + "pulse",
1024 priority,
1025 localhost->rrd_update_every,
1026 RRDSET_TYPE_LINE);
@@ -1056,7 +1056,7 @@ void telemetry_dbengine_do(bool extended) {
1056 "Netdata Queries Running",
1057 "queries",
1058 "netdata",
1059 - "stats",
1059 + "pulse",
1060 priority,
1061 localhost->rrd_update_every,
1062 RRDSET_TYPE_LINE);
@@ -1086,7 +1086,7 @@ void telemetry_dbengine_do(bool extended) {
1086 "Netdata Query Pages Metadata Source",
1087 "pages/s",
1088 "netdata",
1089 - "stats",
1089 + "pulse",
1090 priority,
1091 localhost->rrd_update_every,
1092 RRDSET_TYPE_STACKED);
@@ -1120,7 +1120,7 @@ void telemetry_dbengine_do(bool extended) {
1120 "Netdata Query Pages to Data Source",
1121 "pages/s",
1122 "netdata",
1123 - "stats",
1123 + "pulse",
1124 priority,
1125 localhost->rrd_update_every,
1126 RRDSET_TYPE_STACKED);
@@ -1156,7 +1156,7 @@ void telemetry_dbengine_do(bool extended) {
1156 "Netdata Query Next Page",
1157 "pages/s",
1158 "netdata",
1159 - "stats",
1159 + "pulse",
1160 priority,
1161 localhost->rrd_update_every,
1162 RRDSET_TYPE_STACKED);
@@ -1197,7 +1197,7 @@ void telemetry_dbengine_do(bool extended) {
1197 "Netdata Query Next Page Issues",
1198 "pages/s",
1199 "netdata",
1200 - "stats",
1200 + "pulse",
1201 priority,
1202 localhost->rrd_update_every,
1203 RRDSET_TYPE_STACKED);
@@ -1244,7 +1244,7 @@ void telemetry_dbengine_do(bool extended) {
1244 "Netdata Query Pages Loaded from Disk",
1245 "pages/s",
1246 "netdata",
1247 - "stats",
1247 + "pulse",
1248 priority,
1249 localhost->rrd_update_every,
1250 RRDSET_TYPE_LINE);
@@ -1296,7 +1296,7 @@ void telemetry_dbengine_do(bool extended) {
1296 "Netdata Database Events",
1297 "events/s",
1298 "netdata",
1299 - "stats",
1299 + "pulse",
1300 priority,
1301 localhost->rrd_update_every,
1302 RRDSET_TYPE_LINE);
@@ -1340,7 +1340,7 @@ void telemetry_dbengine_do(bool extended) {
1340 "Netdata Query Preparation Timings",
1341 "usec/s",
1342 "netdata",
1343 - "stats",
1343 + "pulse",
1344 priority,
1345 localhost->rrd_update_every,
1346 RRDSET_TYPE_STACKED);
@@ -1381,7 +1381,7 @@ void telemetry_dbengine_do(bool extended) {
1381 "Netdata Query Timings",
1382 "usec/s",
1383 "netdata",
1384 - "stats",
1384 + "pulse",
1385 priority,
1386 localhost->rrd_update_every,
1387 RRDSET_TYPE_STACKED);
@@ -1438,7 +1438,7 @@ void telemetry_dbengine_do(bool extended) {
1438 rrd_rdunlock();
1439
1440 if (dbengine_contexts) {
1441 - /* deduplicate telemetry by getting the ones from the last context */
1441 + /* deduplicate by getting the ones from the last context */
1442 stats_array[30] = local_stats_array[30];
1443 stats_array[31] = local_stats_array[31];
1444 stats_array[32] = local_stats_array[32];
@@ -1461,7 +1461,7 @@ void telemetry_dbengine_do(bool extended) {
1461 "Netdata DB engine data extents' compression savings ratio",
1462 "percentage",
1463 "netdata",
1464 - "stats",
1464 + "pulse",
1465 priority,
1466 localhost->rrd_update_every,
1467 RRDSET_TYPE_LINE);
@@ -1502,7 +1502,7 @@ void telemetry_dbengine_do(bool extended) {
1502 "Netdata DB engine I/O throughput",
1503 "MiB/s",
1504 "netdata",
1505 - "stats",
1505 + "pulse",
1506 priority,
1507 localhost->rrd_update_every,
1508 RRDSET_TYPE_LINE);
@@ -1534,7 +1534,7 @@ void telemetry_dbengine_do(bool extended) {
1534 "Netdata DB engine I/O operations",
1535 "operations/s",
1536 "netdata",
1537 - "stats",
1537 + "pulse",
1538 priority,
1539 localhost->rrd_update_every,
1540 RRDSET_TYPE_LINE);
@@ -1567,7 +1567,7 @@ void telemetry_dbengine_do(bool extended) {
1567 "Netdata DB engine errors",
1568 "errors/s",
1569 "netdata",
1570 - "stats",
1570 + "pulse",
1571 priority,
1572 localhost->rrd_update_every,
1573 RRDSET_TYPE_LINE);
@@ -1602,7 +1602,7 @@ void telemetry_dbengine_do(bool extended) {
1602 "Netdata DB engine File Descriptors",
1603 "descriptors",
1604 "netdata",
1605 - "stats",
1605 + "pulse",
1606 priority,
1607 localhost->rrd_update_every,
1608 RRDSET_TYPE_LINE);
src/daemon/pulse/pulse-dbengine.h new
+17
@@ -0,0 +1,17 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_DBENGINE_H
4 +#define NETDATA_PULSE_DBENGINE_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#if defined(PULSE_INTERNALS)
9 +extern size_t pulse_dbengine_total_memory;
10 +
11 +#if defined(ENABLE_DBENGINE)
12 +void pulse_dbengine_do(bool extended);
13 +#endif
14 +
15 +#endif
16 +
17 +#endif //NETDATA_PULSE_DBENGINE_H
src/daemon/pulse/pulse-dictionary.c renamed
+9 -9
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-dictionary.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-dictionary.h"
5
6 struct dictionary_stats dictionary_stats_category_collectors = { .name = "collectors" };
7 struct dictionary_stats dictionary_stats_category_rrdhost = { .name = "rrdhost" };
@@ -104,7 +104,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
104 , "Dictionaries"
105 , "dictionaries"
106 , "netdata"
107 - , "stats"
107 + , "pulse"
108 , priority + 0
109 , localhost->rrd_update_every
110 , RRDSET_TYPE_LINE
@@ -145,7 +145,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
145 , "Dictionary Items"
146 , "items"
147 , "netdata"
148 - , "stats"
148 + , "pulse"
149 , priority + 1
150 , localhost->rrd_update_every
151 , RRDSET_TYPE_LINE
@@ -195,7 +195,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
195 , "Dictionary Operations"
196 , "ops/s"
197 , "netdata"
198 - , "stats"
198 + , "pulse"
199 , priority + 2
200 , localhost->rrd_update_every
201 , RRDSET_TYPE_LINE
@@ -254,7 +254,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
254 , "Dictionary Callbacks"
255 , "callbacks/s"
256 , "netdata"
257 - , "stats"
257 + , "pulse"
258 , priority + 3
259 , localhost->rrd_update_every
260 , RRDSET_TYPE_LINE
@@ -300,7 +300,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
300 , "Dictionary Memory"
301 , "bytes"
302 , "netdata"
303 - , "stats"
303 + , "pulse"
304 , priority + 4
305 , localhost->rrd_update_every
306 , RRDSET_TYPE_STACKED
@@ -345,7 +345,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
345 , "Dictionary Spins"
346 , "count"
347 , "netdata"
348 - , "stats"
348 + , "pulse"
349 , priority + 5
350 , localhost->rrd_update_every
351 , RRDSET_TYPE_LINE
@@ -368,7 +368,7 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
368 }
369 }
370
371 -void telemetry_dictionary_do(bool extended) {
371 +void pulse_dictionary_do(bool extended) {
372 if(!extended) return;
373
374 for(int i = 0; dictionary_categories[i].stats ;i++) {
src/daemon/pulse/pulse-dictionary.h renamed
+5 -5
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#ifndef NETDATA_TELEMETRY_DICTIONARY_H
4 -#define NETDATA_TELEMETRY_DICTIONARY_H
3 +#ifndef NETDATA_PULSE_DICTIONARY_H
4 +#define NETDATA_PULSE_DICTIONARY_H
5
6 #include "daemon/common.h"
7
@@ -17,8 +17,8 @@ extern struct dictionary_stats dictionary_stats_category_replication;
17
18 extern size_t rrddim_db_memory_size;
19
20 -#if defined(TELEMETRY_INTERNALS)
21 -void telemetry_dictionary_do(bool extended);
20 +#if defined(PULSE_INTERNALS)
21 +void pulse_dictionary_do(bool extended);
22 #endif
23
24 -#endif //NETDATA_TELEMETRY_DICTIONARY_H
24 +#endif //NETDATA_PULSE_DICTIONARY_H
src/daemon/pulse/pulse-gorilla.c renamed
+7 -7
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-gorilla.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-gorilla.h"
5
6 static struct gorilla_statistics {
7 bool enabled;
@@ -13,13 +13,13 @@ static struct gorilla_statistics {
13 alignas(64) uint64_t gorilla_tier0_disk_original_bytes;
14 } gorilla_statistics = { 0 };
15
16 -void telemetry_gorilla_hot_buffer_added() {
16 +void pulse_gorilla_hot_buffer_added() {
17 if(!gorilla_statistics.enabled) return;
18
19 __atomic_fetch_add(&gorilla_statistics.tier0_hot_gorilla_buffers, 1, __ATOMIC_RELAXED);
20 }
21
22 -void telemetry_gorilla_tier0_page_flush(uint32_t actual, uint32_t optimal, uint32_t original) {
22 +void pulse_gorilla_tier0_page_flush(uint32_t actual, uint32_t optimal, uint32_t original) {
23 if(!gorilla_statistics.enabled) return;
24
25 __atomic_fetch_add(&gorilla_statistics.gorilla_tier0_disk_actual_bytes, actual, __ATOMIC_RELAXED);
@@ -34,7 +34,7 @@ static inline void global_statistics_copy(struct gorilla_statistics *gs) {
34 gs->gorilla_tier0_disk_original_bytes = __atomic_load_n(&gorilla_statistics.gorilla_tier0_disk_original_bytes, __ATOMIC_RELAXED);
35 }
36
37 -void telemetry_gorilla_do(bool extended __maybe_unused) {
37 +void pulse_gorilla_do(bool extended __maybe_unused) {
38 #ifdef ENABLE_DBENGINE
39 if(!extended) return;
40 gorilla_statistics.enabled = true;
@@ -57,7 +57,7 @@ void telemetry_gorilla_do(bool extended __maybe_unused) {
57 , "Number of gorilla_pages"
58 , "count"
59 , "netdata"
60 - , "stats"
60 + , "pulse"
61 , 131004
62 , localhost->rrd_update_every
63 , RRDSET_TYPE_LINE
@@ -89,7 +89,7 @@ void telemetry_gorilla_do(bool extended __maybe_unused) {
89 , "DBENGINE Gorilla Compression Efficiency on Tier 0"
90 , "bytes"
91 , "netdata"
92 - , "stats"
92 + , "pulse"
93 , 131005
94 , localhost->rrd_update_every
95 , RRDSET_TYPE_LINE
src/daemon/pulse/pulse-gorilla.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_GORILLA_H
4 +#define NETDATA_PULSE_GORILLA_H
5 +
6 +#include "daemon/common.h"
7 +
8 +void pulse_gorilla_hot_buffer_added();
9 +void pulse_gorilla_tier0_page_flush(uint32_t actual, uint32_t optimal, uint32_t original);
10 +
11 +#if defined(PULSE_INTERNALS)
12 +void pulse_gorilla_do(bool extended);
13 +#endif
14 +
15 +#endif //NETDATA_PULSE_GORILLA_H
src/daemon/pulse/pulse-heartbeat.c renamed
+4 -4
@@ -1,9 +1,9 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-heartbeat.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-heartbeat.h"
5
6 -void telemetry_heartbeat_do(bool extended) {
6 +void pulse_heartbeat_do(bool extended) {
7 if(!extended) return;
8
9 static RRDSET *st_heartbeat = NULL;
@@ -21,7 +21,7 @@ void telemetry_heartbeat_do(bool extended) {
21 , "System clock jitter"
22 , "microseconds"
23 , "netdata"
24 - , "stats"
24 + , "pulse"
25 , 900000
26 , localhost->rrd_update_every
27 , RRDSET_TYPE_AREA);
src/daemon/pulse/pulse-heartbeat.h new
+12
@@ -0,0 +1,12 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_HEARTBEAT_H
4 +#define NETDATA_PULSE_HEARTBEAT_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#if defined(PULSE_INTERNALS)
9 +void pulse_heartbeat_do(bool extended);
10 +#endif
11 +
12 +#endif //NETDATA_PULSE_HEARTBEAT_H
src/daemon/pulse/pulse-http-api.c renamed
+14 -14
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-http-api.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-http-api.h"
5
6 #define GLOBAL_STATS_RESET_WEB_USEC_MAX 0x01
7
@@ -21,16 +21,16 @@ static struct web_statistics {
21 uint64_t content_size_compressed;
22 } web_statistics;
23
24 -uint64_t telemetry_web_client_connected(void) {
24 +uint64_t pulse_web_client_connected(void) {
25 __atomic_fetch_add(&web_statistics.connected_clients, 1, __ATOMIC_RELAXED);
26 return __atomic_fetch_add(&web_statistics.web_client_count, 1, __ATOMIC_RELAXED);
27 }
28
29 -void telemetry_web_client_disconnected(void) {
29 +void pulse_web_client_disconnected(void) {
30 __atomic_fetch_sub(&web_statistics.connected_clients, 1, __ATOMIC_RELAXED);
31 }
32
33 -void telemetry_web_request_completed(uint64_t dt,
33 +void pulse_web_request_completed(uint64_t dt,
34 uint64_t bytes_received,
35 uint64_t bytes_sent,
36 uint64_t content_size,
@@ -47,7 +47,7 @@ void telemetry_web_request_completed(uint64_t dt,
47 __atomic_fetch_add(&web_statistics.content_size_compressed, compressed_content_size, __ATOMIC_RELAXED);
48 }
49
50 -static inline void telemetry_web_copy(struct web_statistics *gs, uint8_t options) {
50 +static inline void pulse_web_copy(struct web_statistics *gs, uint8_t options) {
51 gs->connected_clients = __atomic_load_n(&web_statistics.connected_clients, __ATOMIC_RELAXED);
52 gs->web_requests = __atomic_load_n(&web_statistics.web_requests, __ATOMIC_RELAXED);
53 gs->web_usec = __atomic_load_n(&web_statistics.web_usec, __ATOMIC_RELAXED);
@@ -64,9 +64,9 @@ static inline void telemetry_web_copy(struct web_statistics *gs, uint8_t options
64 }
65 }
66
67 -void telemetry_web_do(bool extended) {
67 +void pulse_web_do(bool extended) {
68 static struct web_statistics gs;
69 - telemetry_web_copy(&gs, GLOBAL_STATS_RESET_WEB_USEC_MAX);
69 + pulse_web_copy(&gs, GLOBAL_STATS_RESET_WEB_USEC_MAX);
70
71 // ----------------------------------------------------------------
72
@@ -84,7 +84,7 @@ void telemetry_web_do(bool extended) {
84 , "Netdata Web API Clients"
85 , "connected clients"
86 , "netdata"
87 - , "stats"
87 + , "pulse"
88 , 130200
89 , localhost->rrd_update_every
90 , RRDSET_TYPE_LINE
@@ -113,7 +113,7 @@ void telemetry_web_do(bool extended) {
113 , "Netdata Web API Requests Received"
114 , "requests/s"
115 , "netdata"
116 - , "stats"
116 + , "pulse"
117 , 130300
118 , localhost->rrd_update_every
119 , RRDSET_TYPE_LINE
@@ -143,7 +143,7 @@ void telemetry_web_do(bool extended) {
143 , "Netdata Web API Network Traffic"
144 , "kilobits/s"
145 , "netdata"
146 - , "stats"
146 + , "pulse"
147 , 130400
148 , localhost->rrd_update_every
149 , RRDSET_TYPE_AREA
@@ -178,7 +178,7 @@ void telemetry_web_do(bool extended) {
178 , "Netdata Web API Response Time"
179 , "milliseconds/request"
180 , "netdata"
181 - , "stats"
181 + , "pulse"
182 , 130500
183 , localhost->rrd_update_every
184 , RRDSET_TYPE_LINE
@@ -232,7 +232,7 @@ void telemetry_web_do(bool extended) {
232 , "Netdata Web API Responses Compression Savings Ratio"
233 , "percentage"
234 , "netdata"
235 - , "stats"
235 + , "pulse"
236 , 130600
237 , localhost->rrd_update_every
238 , RRDSET_TYPE_LINE
@@ -241,7 +241,7 @@ void telemetry_web_do(bool extended) {
241 rd_savings = rrddim_add(st_compression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
242 }
243
244 - // since we don't lock here to read the telemetry
244 + // since we don't lock here to read the data
245 // read the smaller value first
246 unsigned long long gcompressed_content_size = gs.content_size_compressed;
247 unsigned long long gcontent_size = gs.content_size_uncompressed;
src/daemon/pulse/pulse-http-api.h renamed
+8 -8
@@ -1,21 +1,21 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#ifndef NETDATA_TELEMETRY_HTTP_API_H
4 -#define NETDATA_TELEMETRY_HTTP_API_H
3 +#ifndef NETDATA_PULSE_HTTP_API_H
4 +#define NETDATA_PULSE_HTTP_API_H
5
6 #include "daemon/common.h"
7
8 -uint64_t telemetry_web_client_connected(void);
9 -void telemetry_web_client_disconnected(void);
8 +uint64_t pulse_web_client_connected(void);
9 +void pulse_web_client_disconnected(void);
10
11 -void telemetry_web_request_completed(uint64_t dt,
11 +void pulse_web_request_completed(uint64_t dt,
12 uint64_t bytes_received,
13 uint64_t bytes_sent,
14 uint64_t content_size,
15 uint64_t compressed_content_size);
16
17 -#if defined(TELEMETRY_INTERNALS)
18 -void telemetry_web_do(bool extended);
17 +#if defined(PULSE_INTERNALS)
18 +void pulse_web_do(bool extended);
19 #endif
20
21 -#endif //NETDATA_TELEMETRY_HTTP_API_H
21 +#endif //NETDATA_PULSE_HTTP_API_H
src/daemon/pulse/pulse-ingestion.c renamed
+7 -7
@@ -1,27 +1,27 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-ingestion.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-ingestion.h"
5
6 static struct ingest_statistics {
7 uint64_t db_points_stored_per_tier[RRD_STORAGE_TIERS];
8 } ingest_statistics;
9
10 -void telemetry_queries_rrdset_collection_completed(size_t *points_read_per_tier_array) {
10 +void pulse_queries_rrdset_collection_completed(size_t *points_read_per_tier_array) {
11 for(size_t tier = 0; tier < storage_tiers ;tier++) {
12 __atomic_fetch_add(&ingest_statistics.db_points_stored_per_tier[tier], points_read_per_tier_array[tier], __ATOMIC_RELAXED);
13 points_read_per_tier_array[tier] = 0;
14 }
15 }
16
17 -static inline void telemetry_ingestion_copy(struct ingest_statistics *gs) {
17 +static inline void pulse_ingestion_copy(struct ingest_statistics *gs) {
18 for(size_t tier = 0; tier < storage_tiers ;tier++)
19 gs->db_points_stored_per_tier[tier] = __atomic_load_n(&ingest_statistics.db_points_stored_per_tier[tier], __ATOMIC_RELAXED);
20 }
21
22 -void telemetry_ingestion_do(bool extended __maybe_unused) {
22 +void pulse_ingestion_do(bool extended __maybe_unused) {
23 static struct ingest_statistics gs;
24 - telemetry_ingestion_copy(&gs);
24 + pulse_ingestion_copy(&gs);
25
26 {
27 static RRDSET *st_points_stored = NULL;
@@ -37,7 +37,7 @@ void telemetry_ingestion_do(bool extended __maybe_unused) {
37 , "Netdata Time-Series Collected Samples"
38 , "samples/s"
39 , "netdata"
40 - , "stats"
40 + , "pulse"
41 , 131003
42 , localhost->rrd_update_every
43 , RRDSET_TYPE_STACKED
src/daemon/pulse/pulse-ingestion.h new
+14
@@ -0,0 +1,14 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_INGESTION_H
4 +#define NETDATA_PULSE_INGESTION_H
5 +
6 +#include "daemon/common.h"
7 +
8 +void pulse_queries_rrdset_collection_completed(size_t *points_read_per_tier_array);
9 +
10 +#if defined(PULSE_INTERNALS)
11 +void pulse_ingestion_do(bool extended);
12 +#endif
13 +
14 +#endif //NETDATA_PULSE_INGESTION_H
src/daemon/pulse/pulse-ml.c renamed
+10 -10
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-ml.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-ml.h"
5
6 static struct ml_statistics {
7 alignas(64) uint64_t ml_models_consulted;
@@ -14,17 +14,17 @@ static struct ml_statistics {
14 alignas(64) uint64_t ml_memory_delete;
15 } ml_statistics = {0};
16
17 -void telemetry_ml_models_received()
17 +void pulse_ml_models_received()
18 {
19 __atomic_fetch_add(&ml_statistics.ml_models_received, 1, __ATOMIC_RELAXED);
20 }
21
22 -void telemetry_ml_models_ignored()
22 +void pulse_ml_models_ignored()
23 {
24 __atomic_fetch_add(&ml_statistics.ml_models_ignored, 1, __ATOMIC_RELAXED);
25 }
26
27 -void telemetry_ml_models_sent()
27 +void pulse_ml_models_sent()
28 {
29 __atomic_fetch_add(&ml_statistics.ml_models_sent, 1, __ATOMIC_RELAXED);
30 }
@@ -34,24 +34,24 @@ void global_statistics_ml_models_deserialization_failures()
34 __atomic_fetch_add(&ml_statistics.ml_models_deserialization_failures, 1, __ATOMIC_RELAXED);
35 }
36
37 -void telemetry_ml_models_consulted(size_t models_consulted)
37 +void pulse_ml_models_consulted(size_t models_consulted)
38 {
39 __atomic_fetch_add(&ml_statistics.ml_models_consulted, models_consulted, __ATOMIC_RELAXED);
40 }
41
42 -void telemetry_ml_memory_allocated(size_t n)
42 +void pulse_ml_memory_allocated(size_t n)
43 {
44 __atomic_fetch_add(&ml_statistics.ml_memory_consumption, n, __ATOMIC_RELAXED);
45 __atomic_fetch_add(&ml_statistics.ml_memory_new, 1, __ATOMIC_RELAXED);
46 }
47
48 -void telemetry_ml_memory_freed(size_t n)
48 +void pulse_ml_memory_freed(size_t n)
49 {
50 __atomic_fetch_sub(&ml_statistics.ml_memory_consumption, n, __ATOMIC_RELAXED);
51 __atomic_fetch_add(&ml_statistics.ml_memory_delete, 1, __ATOMIC_RELAXED);
52 }
53
54 -uint64_t telemetry_ml_get_current_memory_usage(void) {
54 +uint64_t pulse_ml_get_current_memory_usage(void) {
55 return __atomic_load_n(&ml_statistics.ml_memory_consumption, __ATOMIC_RELAXED);
56 }
57
@@ -69,7 +69,7 @@ static inline void ml_statistics_copy(struct ml_statistics *gs)
69 gs->ml_memory_delete = __atomic_load_n(&ml_statistics.ml_memory_delete, __ATOMIC_RELAXED);
70 }
71
72 -void telemetry_ml_do(bool extended)
72 +void pulse_ml_do(bool extended)
73 {
74 if (!extended)
75 return;
src/daemon/pulse/pulse-ml.h new
+33
@@ -0,0 +1,33 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_ML_H
4 +#define NETDATA_PULSE_ML_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#ifdef __cplusplus
9 +extern "C" {
10 +#endif
11 +
12 +void pulse_ml_models_consulted(size_t models_consulted);
13 +void pulse_ml_models_received();
14 +void pulse_ml_models_ignored();
15 +void pulse_ml_models_sent();
16 +
17 +void pulse_ml_memory_allocated(size_t n);
18 +void pulse_ml_memory_freed(size_t n);
19 +
20 +void global_statistics_ml_models_deserialization_failures();
21 +
22 +uint64_t pulse_ml_get_current_memory_usage(void);
23 +
24 +#if defined(PULSE_INTERNALS)
25 +void pulse_ml_do(bool extended);
26 +#endif
27 +
28 +#ifdef __cplusplus
29 +}
30 +#endif
31 +
32 +
33 +#endif //NETDATA_PULSE_ML_H
src/daemon/pulse/pulse-queries.c renamed
+13 -12
@@ -1,7 +1,8 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-queries.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-queries.h"
5 +#include "streaming/replication.h"
6
7 static struct query_statistics {
8 uint64_t api_data_queries_made;
@@ -31,22 +32,22 @@ static struct query_statistics {
32 uint64_t exporters_db_points_read;
33 } query_statistics;
34
34 -void telemetry_queries_ml_query_completed(size_t points_read) {
35 +void pulse_queries_ml_query_completed(size_t points_read) {
36 __atomic_fetch_add(&query_statistics.ml_queries_made, 1, __ATOMIC_RELAXED);
37 __atomic_fetch_add(&query_statistics.ml_db_points_read, points_read, __ATOMIC_RELAXED);
38 }
39
39 -void telemetry_queries_exporters_query_completed(size_t points_read) {
40 +void pulse_queries_exporters_query_completed(size_t points_read) {
41 __atomic_fetch_add(&query_statistics.exporters_queries_made, 1, __ATOMIC_RELAXED);
42 __atomic_fetch_add(&query_statistics.exporters_db_points_read, points_read, __ATOMIC_RELAXED);
43 }
44
44 -void telemetry_queries_backfill_query_completed(size_t points_read) {
45 +void pulse_queries_backfill_query_completed(size_t points_read) {
46 __atomic_fetch_add(&query_statistics.backfill_queries_made, 1, __ATOMIC_RELAXED);
47 __atomic_fetch_add(&query_statistics.backfill_db_points_read, points_read, __ATOMIC_RELAXED);
48 }
49
49 -void telemetry_queries_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source) {
50 +void pulse_queries_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source) {
51 switch(query_source) {
52 case QUERY_SOURCE_API_DATA:
53 __atomic_fetch_add(&query_statistics.api_data_queries_made, queries, __ATOMIC_RELAXED);
@@ -85,7 +86,7 @@ void telemetry_queries_rrdr_query_completed(size_t queries, uint64_t db_points_r
86 }
87 }
88
88 -static inline void telemetry_queries_copy(struct query_statistics *gs) {
89 +static inline void pulse_queries_copy(struct query_statistics *gs) {
90 gs->api_data_queries_made = __atomic_load_n(&query_statistics.api_data_queries_made, __ATOMIC_RELAXED);
91 gs->api_data_db_points_read = __atomic_load_n(&query_statistics.api_data_db_points_read, __ATOMIC_RELAXED);
92 gs->api_data_result_points_generated = __atomic_load_n(&query_statistics.api_data_result_points_generated, __ATOMIC_RELAXED);
@@ -112,9 +113,9 @@ static inline void telemetry_queries_copy(struct query_statistics *gs) {
113 gs->backfill_db_points_read = __atomic_load_n(&query_statistics.backfill_db_points_read, __ATOMIC_RELAXED);
114 }
115
115 -void telemetry_queries_do(bool extended __maybe_unused) {
116 +void pulse_queries_do(bool extended __maybe_unused) {
117 static struct query_statistics gs;
117 - telemetry_queries_copy(&gs);
118 + pulse_queries_copy(&gs);
119
120 struct replication_query_statistics replication = replication_get_query_statistics();
121
@@ -139,7 +140,7 @@ void telemetry_queries_do(bool extended __maybe_unused) {
140 , "Netdata Time-Series DB Queries"
141 , "queries/s"
142 , "netdata"
142 - , "stats"
143 + , "pulse"
144 , 131000
145 , localhost->rrd_update_every
146 , RRDSET_TYPE_STACKED
@@ -188,7 +189,7 @@ void telemetry_queries_do(bool extended __maybe_unused) {
189 , "Netdata Time-Series DB Samples Read"
190 , "points/s"
191 , "netdata"
191 - , "stats"
192 + , "pulse"
193 , 131001
194 , localhost->rrd_update_every
195 , RRDSET_TYPE_STACKED
@@ -235,7 +236,7 @@ void telemetry_queries_do(bool extended __maybe_unused) {
236 , "Netdata Time-Series Samples Generated"
237 , "points/s"
238 , "netdata"
238 - , "stats"
239 + , "pulse"
240 , 131002
241 , localhost->rrd_update_every
242 , RRDSET_TYPE_STACKED
src/daemon/pulse/pulse-queries.h new
+17
@@ -0,0 +1,17 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_QUERIES_H
4 +#define NETDATA_PULSE_QUERIES_H
5 +
6 +#include "daemon/common.h"
7 +
8 +void pulse_queries_ml_query_completed(size_t points_read);
9 +void pulse_queries_exporters_query_completed(size_t points_read);
10 +void pulse_queries_backfill_query_completed(size_t points_read);
11 +void pulse_queries_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source);
12 +
13 +#if defined(PULSE_INTERNALS)
14 +void pulse_queries_do(bool extended);
15 +#endif
16 +
17 +#endif //NETDATA_PULSE_QUERIES_H
src/daemon/pulse/pulse-sqlite3.c renamed
+10 -10
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-sqlite3.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-sqlite3.h"
5
6 static struct sqlite3_statistics {
7 bool enabled;
@@ -22,7 +22,7 @@ static struct sqlite3_statistics {
22 alignas(64) uint64_t sqlite3_context_cache_write;
23 } sqlite3_statistics = { };
24
25 -void telemetry_sqlite3_query_completed(bool success, bool busy, bool locked) {
25 +void pulse_sqlite3_query_completed(bool success, bool busy, bool locked) {
26 if(!sqlite3_statistics.enabled) return;
27
28 __atomic_fetch_add(&sqlite3_statistics.sqlite3_queries_made, 1, __ATOMIC_RELAXED);
@@ -41,7 +41,7 @@ void telemetry_sqlite3_query_completed(bool success, bool busy, bool locked) {
41 }
42 }
43
44 -void telemetry_sqlite3_row_completed(void) {
44 +void pulse_sqlite3_row_completed(void) {
45 if(!sqlite3_statistics.enabled) return;
46
47 __atomic_fetch_add(&sqlite3_statistics.sqlite3_rows, 1, __ATOMIC_RELAXED);
@@ -123,7 +123,7 @@ static inline void sqlite3_statistics_copy(struct sqlite3_statistics *gs) {
123 last_run = now_monotonic_usec();
124 }
125
126 -void telemetry_sqlite3_do(bool extended) {
126 +void pulse_sqlite3_do(bool extended) {
127 if(!extended) return;
128 sqlite3_statistics.enabled = true;
129
@@ -144,7 +144,7 @@ void telemetry_sqlite3_do(bool extended) {
144 , "Netdata SQLite3 Queries"
145 , "queries/s"
146 , "netdata"
147 - , "stats"
147 + , "pulse"
148 , 131100
149 , localhost->rrd_update_every
150 , RRDSET_TYPE_LINE
@@ -174,7 +174,7 @@ void telemetry_sqlite3_do(bool extended) {
174 , "Netdata SQLite3 Queries by status"
175 , "queries/s"
176 , "netdata"
177 - , "stats"
177 + , "pulse"
178 , 131101
179 , localhost->rrd_update_every
180 , RRDSET_TYPE_LINE
@@ -210,7 +210,7 @@ void telemetry_sqlite3_do(bool extended) {
210 , "Netdata SQLite3 Rows"
211 , "rows/s"
212 , "netdata"
213 - , "stats"
213 + , "pulse"
214 , 131102
215 , localhost->rrd_update_every
216 , RRDSET_TYPE_LINE
@@ -241,7 +241,7 @@ void telemetry_sqlite3_do(bool extended) {
241 , "Netdata SQLite3 metadata cache"
242 , "ops/s"
243 , "netdata"
244 - , "stats"
244 + , "pulse"
245 , 131103
246 , localhost->rrd_update_every
247 , RRDSET_TYPE_LINE
@@ -285,7 +285,7 @@ void telemetry_sqlite3_do(bool extended) {
285 , "Netdata SQLite3 context cache"
286 , "ops/s"
287 , "netdata"
288 - , "stats"
288 + , "pulse"
289 , 131104
290 , localhost->rrd_update_every
291 , RRDSET_TYPE_LINE
src/daemon/pulse/pulse-sqlite3.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_SQLITE3_H
4 +#define NETDATA_PULSE_SQLITE3_H
5 +
6 +#include "daemon/common.h"
7 +
8 +void pulse_sqlite3_query_completed(bool success, bool busy, bool locked);
9 +void pulse_sqlite3_row_completed(void);
10 +
11 +#if defined(PULSE_INTERNALS)
12 +void pulse_sqlite3_do(bool extended);
13 +#endif
14 +
15 +#endif //NETDATA_PULSE_SQLITE3_H
src/daemon/pulse/pulse-string.c renamed
+6 -6
@@ -1,9 +1,9 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-string.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-string.h"
5
6 -void telemetry_string_do(bool extended) {
6 +void pulse_string_do(bool extended) {
7 if(!extended) return;
8
9 static RRDSET *st_ops = NULL, *st_entries = NULL, *st_mem = NULL;
@@ -28,7 +28,7 @@ void telemetry_string_do(bool extended) {
28 , "Strings operations"
29 , "ops/s"
30 , "netdata"
31 - , "stats"
31 + , "pulse"
32 , 910000
33 , localhost->rrd_update_every
34 , RRDSET_TYPE_LINE);
@@ -61,7 +61,7 @@ void telemetry_string_do(bool extended) {
61 , "Strings entries"
62 , "entries"
63 , "netdata"
64 - , "stats"
64 + , "pulse"
65 , 910001
66 , localhost->rrd_update_every
67 , RRDSET_TYPE_AREA);
@@ -88,7 +88,7 @@ void telemetry_string_do(bool extended) {
88 , "Strings memory"
89 , "bytes"
90 , "netdata"
91 - , "stats"
91 + , "pulse"
92 , 910001
93 , localhost->rrd_update_every
94 , RRDSET_TYPE_AREA);
src/daemon/pulse/pulse-string.h new
+12
@@ -0,0 +1,12 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_STRING_H
4 +#define NETDATA_PULSE_STRING_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#if defined(PULSE_INTERNALS)
9 +void pulse_string_do(bool extended);
10 +#endif
11 +
12 +#endif //NETDATA_PULSE_STRING_H
src/daemon/pulse/pulse-trace-allocations.c renamed
+7 -7
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-trace-allocations.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-trace-allocations.h"
5
6 #ifdef NETDATA_TRACE_ALLOCATIONS
7
@@ -58,7 +58,7 @@ static int do_memory_trace_item(void *item, void *data) {
58 return 1;
59 }
60
61 -void telemetry_trace_allocations_do(bool extended) {
61 +void pulse_trace_allocations_do(bool extended) {
62 if(!extended) return;
63
64 static struct memory_trace_data tmp = {
@@ -78,7 +78,7 @@ void telemetry_trace_allocations_do(bool extended) {
78 , "Netdata Memory Used by Function"
79 , "bytes"
80 , "netdata"
81 - , "stats"
81 + , "pulse"
82 , 900000
83 , localhost->rrd_update_every
84 , RRDSET_TYPE_STACKED
@@ -95,7 +95,7 @@ void telemetry_trace_allocations_do(bool extended) {
95 , "Netdata Memory Operations by Function"
96 , "ops/s"
97 , "netdata"
98 - , "stats"
98 + , "pulse"
99 , 900001
100 , localhost->rrd_update_every
101 , RRDSET_TYPE_LINE
@@ -112,7 +112,7 @@ void telemetry_trace_allocations_do(bool extended) {
112 , "Netdata Memory Allocations by Function"
113 , "allocations"
114 , "netdata"
115 - , "stats"
115 + , "pulse"
116 , 900002
117 , localhost->rrd_update_every
118 , RRDSET_TYPE_STACKED
@@ -129,7 +129,7 @@ void telemetry_trace_allocations_do(bool extended) {
129 , "Netdata Average Allocation Size by Function"
130 , "bytes"
131 , "netdata"
132 - , "stats"
132 + , "pulse"
133 , 900003
134 , localhost->rrd_update_every
135 , RRDSET_TYPE_LINE
src/daemon/pulse/pulse-trace-allocations.h new
+14
@@ -0,0 +1,14 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_TRACE_ALLOCATIONS_H
4 +#define NETDATA_PULSE_TRACE_ALLOCATIONS_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#if defined(PULSE_INTERNALS)
9 +#ifdef NETDATA_TRACE_ALLOCATIONS
10 +void pulse_trace_allocations_do(bool extended);
11 +#endif
12 +#endif
13 +
14 +#endif //NETDATA_PULSE_TRACE_ALLOCATIONS_H
src/daemon/pulse/pulse-workers.c renamed
+266 -29
@@ -1,10 +1,21 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
4 -#include "telemetry-workers.h"
3 +#define PULSE_INTERNALS 1
4 +#include "pulse-workers.h"
5
6 #define WORKERS_MIN_PERCENT_DEFAULT 10000.0
7
8 +struct worker_spinlocks {
9 + size_t locks;
10 + size_t spins;
11 +
12 + RRDDIM *rd_locks;
13 + RRDDIM *rd_spins;
14 +};
15 +
16 +DEFINE_JUDYL_TYPED(SPINLOCKS, struct worker_spinlocks *);
17 +SPINLOCKS_JudyLSet ALL_SPINLOCKS = { 0 };
18 +
19 struct worker_job_type_gs {
20 STRING *name;
21 STRING *units;
@@ -94,10 +105,14 @@ struct worker_utilization {
105 RRDSET *st_workers_busy_per_job_type;
106
107 RRDDIM *rd_total_cpu_utilizaton;
108 +
109 + RRDSET *st_spinlocks_locks;
110 + RRDSET *st_spinlocks_spins;
111 + SPINLOCKS_JudyLSet spinlocks;
112 };
113
114 static struct worker_utilization all_workers_utilization[] = {
100 - { .name = "STATS", .family = "workers telemetry", .priority = 1000000 },
115 + { .name = "PULSE", .family = "workers pulse", .priority = 1000000 },
116 { .name = "HEALTH", .family = "workers health alarms", .priority = 1000000 },
117 { .name = "MLTRAIN", .family = "workers ML training", .priority = 1000000 },
118 { .name = "MLDETECT", .family = "workers ML detection", .priority = 1000000 },
@@ -132,6 +147,108 @@ static struct worker_utilization all_workers_utilization[] = {
147 { .name = NULL, .family = NULL }
148 };
149
150 +static void workers_total_spinlock_contention_chart(void) {
151 + {
152 + static RRDSET *st = NULL;
153 +
154 + if(unlikely(!st)) {
155 + st = rrdset_create_localhost(
156 + "netdata"
157 + , "spinlock_total_locks"
158 + , NULL
159 + , "spinlocks"
160 + , "netdata.spinlock_total_locks"
161 + , "Netdata Total Spinlock Locks"
162 + , "locks"
163 + , "netdata"
164 + , "pulse"
165 + , 920000
166 + , localhost->rrd_update_every
167 + , RRDSET_TYPE_LINE
168 + );
169 + }
170 +
171 + Word_t idx = 0;
172 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&ALL_SPINLOCKS, &idx);
173 + wusp;
174 + wusp = SPINLOCKS_NEXT(&ALL_SPINLOCKS, &idx)) {
175 + const char *func = (const char *)idx;
176 + RRDDIM *rd = rrddim_find(st, func);
177 + if(!rd) rd = rrddim_add(st, func, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
178 + rrddim_set_by_pointer(st, rd, (collected_number)wusp->locks);
179 + }
180 +
181 + rrdset_done(st);
182 + }
183 +
184 + {
185 + static RRDSET *st = NULL;
186 + if(unlikely(!st)) {
187 + st = rrdset_create_localhost(
188 + "netdata"
189 + , "spinlock_total_spins"
190 + , NULL
191 + , "spinlocks"
192 + , "netdata.spinlock_total_spins"
193 + , "Netdata Total Spinlock Spins"
194 + , "spins"
195 + , "netdata"
196 + , "pulse"
197 + , 920001
198 + , localhost->rrd_update_every
199 + , RRDSET_TYPE_LINE
200 + );
201 + }
202 +
203 + Word_t idx = 0;
204 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&ALL_SPINLOCKS, &idx);
205 + wusp;
206 + wusp = SPINLOCKS_NEXT(&ALL_SPINLOCKS, &idx)) {
207 + const char *func = (const char *)idx;
208 + RRDDIM *rd = rrddim_find(st, func);
209 + if(!rd) rd = rrddim_add(st, func, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
210 + rrddim_set_by_pointer(st, rd, (collected_number)wusp->spins);
211 + }
212 +
213 + rrdset_done(st);
214 + }
215 +
216 + {
217 + static RRDSET *st = NULL;
218 + if(unlikely(!st)) {
219 + st = rrdset_create_localhost(
220 + "netdata"
221 + , "spinlock_total_spins_per_lock"
222 + , NULL
223 + , "spinlocks"
224 + , "netdata.spinlock_total_spins_per_lock"
225 + , "Netdata Average Spinlock Spins Per Lock"
226 + , "spins"
227 + , "netdata"
228 + , "pulse"
229 + , 920002
230 + , localhost->rrd_update_every
231 + , RRDSET_TYPE_LINE
232 + );
233 + }
234 +
235 + Word_t idx = 0;
236 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&ALL_SPINLOCKS, &idx);
237 + wusp;
238 + wusp = SPINLOCKS_NEXT(&ALL_SPINLOCKS, &idx)) {
239 + const char *func = (const char *)idx;
240 + RRDDIM *rd = rrddim_find(st, func);
241 + if(!rd) rd = rrddim_add(st, func, NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
242 + if(!wusp->locks)
243 + rrddim_set_by_pointer(st, rd, 0);
244 + else
245 + rrddim_set_by_pointer(st, rd, (collected_number)((uint64_t)wusp->spins * 10000ULL / (uint64_t)wusp->locks));
246 + }
247 +
248 + rrdset_done(st);
249 + }
250 +}
251 +
252 static void workers_total_cpu_utilization_chart(void) {
253 size_t i, cpu_enabled = 0;
254 for(i = 0; all_workers_utilization[i].name ;i++)
@@ -151,7 +268,7 @@ static void workers_total_cpu_utilization_chart(void) {
268 "Netdata Workers CPU Utilization (100% = 1 core)",
269 "%",
270 "netdata",
154 - "stats",
271 + "pulse",
272 999000,
273 localhost->rrd_update_every,
274 RRDSET_TYPE_STACKED);
@@ -199,7 +316,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
316 , "Netdata Workers Busy Time (100% = all workers busy)"
317 , "%"
318 , "netdata"
202 - , "stats"
319 + , "pulse"
320 , wu->priority
321 , localhost->rrd_update_every
322 , RRDSET_TYPE_AREA
@@ -252,7 +369,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
369 , "Netdata Workers CPU Utilization (100% = all workers busy)"
370 , "%"
371 , "netdata"
255 - , "stats"
372 + , "pulse"
373 , wu->priority + 1
374 , localhost->rrd_update_every
375 , RRDSET_TYPE_AREA
@@ -303,7 +420,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
420 , "Netdata Workers Jobs Started by Type"
421 , "jobs"
422 , "netdata"
306 - , "stats"
423 + , "pulse"
424 , wu->priority + 2
425 , localhost->rrd_update_every
426 , RRDSET_TYPE_STACKED
@@ -346,7 +463,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
463 , "Netdata Workers Busy Time by Type"
464 , "ms"
465 , "netdata"
349 - , "stats"
466 + , "pulse"
467 , wu->priority + 3
468 , localhost->rrd_update_every
469 , RRDSET_TYPE_STACKED
@@ -390,7 +507,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
507 , "Netdata Workers Threads"
508 , "threads"
509 , "netdata"
393 - , "stats"
510 + , "pulse"
511 , wu->priority + 4
512 , localhost->rrd_update_every
513 , RRDSET_TYPE_STACKED
@@ -405,6 +522,85 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
522 rrdset_done(wu->st_workers_threads);
523 }
524
525 + // ----------------------------------------------------------------------
526 + // spinlocks
527 +
528 + {
529 + if(unlikely(!wu->st_spinlocks_locks)) {
530 + char name[RRD_ID_LENGTH_MAX + 1];
531 + snprintfz(name, RRD_ID_LENGTH_MAX, "workers_spinlock_locks_%s", wu->name_lowercase);
532 +
533 + char context[RRD_ID_LENGTH_MAX + 1];
534 + snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.spinlock_locks", wu->name_lowercase);
535 +
536 + wu->st_spinlocks_locks = rrdset_create_localhost(
537 + "netdata"
538 + , name
539 + , NULL
540 + , wu->family
541 + , context
542 + , "Netdata Spinlock Locks"
543 + , "locks"
544 + , "netdata"
545 + , "pulse"
546 + , wu->priority + 5
547 + , localhost->rrd_update_every
548 + , RRDSET_TYPE_LINE
549 + );
550 + }
551 +
552 + Word_t idx = 0;
553 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&wu->spinlocks, &idx);
554 + wusp;
555 + wusp = SPINLOCKS_NEXT(&wu->spinlocks, &idx)) {
556 + const char *func = (const char *)idx;
557 + if(!wusp->rd_locks)
558 + wusp->rd_locks = rrddim_add(wu->st_spinlocks_locks, func, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
559 +
560 + rrddim_set_by_pointer(wu->st_spinlocks_locks, wusp->rd_locks, (collected_number)wusp->locks);
561 + }
562 +
563 + rrdset_done(wu->st_spinlocks_locks);
564 + }
565 +
566 + {
567 + if(unlikely(!wu->st_spinlocks_spins)) {
568 + char name[RRD_ID_LENGTH_MAX + 1];
569 + snprintfz(name, RRD_ID_LENGTH_MAX, "workers_spinlock_spins_%s", wu->name_lowercase);
570 +
571 + char context[RRD_ID_LENGTH_MAX + 1];
572 + snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.spinlock_spins", wu->name_lowercase);
573 +
574 + wu->st_spinlocks_spins = rrdset_create_localhost(
575 + "netdata"
576 + , name
577 + , NULL
578 + , wu->family
579 + , context
580 + , "Netdata Spinlock Spins"
581 + , "spins"
582 + , "netdata"
583 + , "pulse"
584 + , wu->priority + 6
585 + , localhost->rrd_update_every
586 + , RRDSET_TYPE_LINE
587 + );
588 + }
589 +
590 + Word_t idx = 0;
591 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&wu->spinlocks, &idx);
592 + wusp;
593 + wusp = SPINLOCKS_NEXT(&wu->spinlocks, &idx)) {
594 + const char *func = (const char *)idx;
595 + if(!wusp->rd_spins)
596 + wusp->rd_spins = rrddim_add(wu->st_spinlocks_spins, func, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
597 +
598 + rrddim_set_by_pointer(wu->st_spinlocks_spins, wusp->rd_spins, (collected_number)wusp->spins);
599 + }
600 +
601 + rrdset_done(wu->st_spinlocks_spins);
602 + }
603 +
604 // ----------------------------------------------------------------------
605 // custom metric types WORKER_METRIC_ABSOLUTE
606
@@ -442,8 +638,8 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
638 , title
639 , (wu->per_job_type[i].units)?string2str(wu->per_job_type[i].units):"value"
640 , "netdata"
445 - , "stats"
446 - , wu->priority + 5 + i
641 + , "pulse"
642 + , wu->priority + 10 + i
643 , localhost->rrd_update_every
644 , RRDSET_TYPE_LINE
645 );
@@ -498,8 +694,8 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
694 , title
695 , (wu->per_job_type[i].units)?string2str(wu->per_job_type[i].units):"rate"
696 , "netdata"
501 - , "stats"
502 - , wu->priority + 5 + i
697 + , "pulse"
698 + , wu->priority + 10 + i
699 , localhost->rrd_update_every
700 , RRDSET_TYPE_LINE
701 );
@@ -519,6 +715,14 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
715 }
716
717 static void workers_utilization_reset_statistics(struct worker_utilization *wu) {
718 + Word_t idx = 0;
719 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&wu->spinlocks, &idx);
720 + wusp;
721 + wusp = SPINLOCKS_NEXT(&wu->spinlocks, &idx)) {
722 + wusp->locks = 0;
723 + wusp->spins = 0;
724 + }
725 +
726 wu->workers_registered = 0;
727 wu->workers_busy = 0;
728 wu->workers_total_busy_time = 0;
@@ -646,19 +850,22 @@ static struct worker_thread *worker_thread_find_or_create(struct worker_utilizat
850 }
851
852 static void worker_utilization_charts_callback(void *ptr
649 - , pid_t pid __maybe_unused
853 + , pid_t pid
854 , const char *thread_tag __maybe_unused
651 - , size_t max_job_id __maybe_unused
652 - , size_t utilization_usec __maybe_unused
653 - , size_t duration_usec __maybe_unused
654 - , size_t jobs_started __maybe_unused
655 - , size_t is_running __maybe_unused
656 - , STRING **job_types_names __maybe_unused
657 - , STRING **job_types_units __maybe_unused
658 - , WORKER_METRIC_TYPE *job_types_metric_types __maybe_unused
659 - , size_t *job_types_jobs_started __maybe_unused
660 - , usec_t *job_types_busy_time __maybe_unused
661 - , NETDATA_DOUBLE *job_types_custom_metrics __maybe_unused
855 + , size_t max_job_id
856 + , size_t utilization_usec
857 + , size_t duration_usec
858 + , size_t jobs_started
859 + , size_t is_running
860 + , STRING **job_types_names
861 + , STRING **job_types_units
862 + , WORKER_METRIC_TYPE *job_types_metric_types
863 + , size_t *job_types_jobs_started
864 + , usec_t *job_types_busy_time
865 + , NETDATA_DOUBLE *job_types_custom_metrics
866 + , const char *spinlock_functions[]
867 + , size_t *spinlock_locks
868 + , size_t *spinlock_spins
869 ) {
870 struct worker_utilization *wu = (struct worker_utilization *)ptr;
871
@@ -693,8 +900,7 @@ static void worker_utilization_charts_callback(void *ptr
900 wu->workers_min_busy_time = util;
901
902 // accumulate per job type statistics
696 - size_t i;
697 - for(i = 0; i <= max_job_id ;i++) {
903 + for(size_t i = 0; i <= max_job_id ;i++) {
904 if(!wu->per_job_type[i].name && job_types_names[i])
905 wu->per_job_type[i].name = string_dup(job_types_names[i]);
906
@@ -739,9 +945,31 @@ static void worker_utilization_charts_callback(void *ptr
945 if(cpu > wu->workers_cpu_max) wu->workers_cpu_max = cpu;
946 }
947 wu->workers_cpu_registered += (wt->cpu_enabled) ? 1 : 0;
948 +
949 + // ----------------------------------------------------------------------------------------------------------------
950 + // spinlock contention
951 +
952 + // spinlocks
953 + for(size_t i = 0; i < WORKER_SPINLOCK_CONTENTION_FUNCTIONS && spinlock_functions[i] ;i++) {
954 + struct worker_spinlocks *wusp = SPINLOCKS_GET(&wu->spinlocks, (Word_t)spinlock_functions[i]);
955 + if(!wusp) {
956 + wusp = callocz(1, sizeof(*wusp));
957 + SPINLOCKS_SET(&wu->spinlocks, (Word_t)spinlock_functions[i], wusp);
958 + }
959 + wusp->locks += spinlock_locks[i];
960 + wusp->spins += spinlock_spins[i];
961 +
962 + wusp = SPINLOCKS_GET(&ALL_SPINLOCKS, (Word_t)spinlock_functions[i]);
963 + if(!wusp) {
964 + wusp = callocz(1, sizeof(*wusp));
965 + SPINLOCKS_SET(&ALL_SPINLOCKS, (Word_t)spinlock_functions[i], wusp);
966 + }
967 + wusp->locks += spinlock_locks[i];
968 + wusp->spins += spinlock_spins[i];
969 + }
970 }
971
744 -void telemetry_workers_cleanup(void) {
972 +void pulse_workers_cleanup(void) {
973 int i, j;
974 for(i = 0; all_workers_utilization[i].name ;i++) {
975 struct worker_utilization *wu = &all_workers_utilization[i];
@@ -769,12 +997,20 @@ void telemetry_workers_cleanup(void) {
997 }
998 }
999
772 -void telemetry_workers_do(bool extended) {
1000 +void pulse_workers_do(bool extended) {
1001 if(!extended) return;
1002
1003 static size_t iterations = 0;
1004 iterations++;
1005
1006 + Word_t idx = 0;
1007 + for(struct worker_spinlocks *wusp = SPINLOCKS_FIRST(&ALL_SPINLOCKS, &idx);
1008 + wusp;
1009 + wusp = SPINLOCKS_NEXT(&ALL_SPINLOCKS, &idx)) {
1010 + wusp->locks = 0;
1011 + wusp->spins = 0;
1012 + }
1013 +
1014 for(int i = 0; all_workers_utilization[i].name ;i++) {
1015 workers_utilization_reset_statistics(&all_workers_utilization[i]);
1016
@@ -788,4 +1024,5 @@ void telemetry_workers_do(bool extended) {
1024 }
1025
1026 workers_total_cpu_utilization_chart();
1027 + workers_total_spinlock_contention_chart();
1028 }
src/daemon/pulse/pulse-workers.h new
+13
@@ -0,0 +1,13 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_WORKERS_H
4 +#define NETDATA_PULSE_WORKERS_H
5 +
6 +#include "daemon/common.h"
7 +
8 +#if defined(PULSE_INTERNALS)
9 +void pulse_workers_do(bool extended);
10 +void pulse_workers_cleanup(void);
11 +#endif
12 +
13 +#endif //NETDATA_PULSE_WORKERS_H
src/daemon/pulse/pulse.c renamed
+52 -52
@@ -1,18 +1,18 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define TELEMETRY_INTERNALS 1
3 +#define PULSE_INTERNALS 1
4 #include "daemon/common.h"
5
6 -#define WORKER_JOB_TELEMETRY_DAEMON 0
6 +#define WORKER_JOB_DAEMON 0
7 #define WORKER_JOB_SQLITE3 1
8 -#define WORKER_JOB_TELEMETRY_HTTP_API 2
9 -#define WORKER_JOB_TELEMETRY_QUERIES 3
10 -#define WORKER_JOB_TELEMETRY_INGESTION 4
8 +#define WORKER_JOB_HTTP_API 2
9 +#define WORKER_JOB_QUERIES 3
10 +#define WORKER_JOB_INGESTION 4
11 #define WORKER_JOB_DBENGINE 5
12 #define WORKER_JOB_STRINGS 6
13 #define WORKER_JOB_DICTIONARIES 7
14 -#define WORKER_JOB_TELEMETRY_ML 8
15 -#define WORKER_JOB_TELEMETRY_GORILLA 9
14 +#define WORKER_JOB_ML 8
15 +#define WORKER_JOB_GORILLA 9
16 #define WORKER_JOB_HEARTBEAT 10
17 #define WORKER_JOB_WORKERS 11
18 #define WORKER_JOB_MALLOC_TRACE 12
@@ -23,22 +23,22 @@
23 #error "WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 14"
24 #endif
25
26 -bool telemetry_enabled = true;
27 -bool telemetry_extended_enabled = false;
26 +bool pulse_enabled = true;
27 +bool pulse_extended_enabled = false;
28
29 -static void telemetry_register_workers(void) {
30 - worker_register("STATS");
29 +static void pulse_register_workers(void) {
30 + worker_register("PULSE");
31
32 - worker_register_job_name(WORKER_JOB_TELEMETRY_DAEMON, "daemon");
32 + worker_register_job_name(WORKER_JOB_DAEMON, "daemon");
33 worker_register_job_name(WORKER_JOB_SQLITE3, "sqlite3");
34 - worker_register_job_name(WORKER_JOB_TELEMETRY_HTTP_API, "http-api");
35 - worker_register_job_name(WORKER_JOB_TELEMETRY_QUERIES, "queries");
36 - worker_register_job_name(WORKER_JOB_TELEMETRY_INGESTION, "ingestion");
34 + worker_register_job_name(WORKER_JOB_HTTP_API, "http-api");
35 + worker_register_job_name(WORKER_JOB_QUERIES, "queries");
36 + worker_register_job_name(WORKER_JOB_INGESTION, "ingestion");
37 worker_register_job_name(WORKER_JOB_DBENGINE, "dbengine");
38 worker_register_job_name(WORKER_JOB_STRINGS, "strings");
39 worker_register_job_name(WORKER_JOB_DICTIONARIES, "dictionaries");
40 - worker_register_job_name(WORKER_JOB_TELEMETRY_ML, "ML");
41 - worker_register_job_name(WORKER_JOB_TELEMETRY_GORILLA, "gorilla");
40 + worker_register_job_name(WORKER_JOB_ML, "ML");
41 + worker_register_job_name(WORKER_JOB_GORILLA, "gorilla");
42 worker_register_job_name(WORKER_JOB_HEARTBEAT, "heartbeat");
43 worker_register_job_name(WORKER_JOB_WORKERS, "workers");
44 worker_register_job_name(WORKER_JOB_MALLOC_TRACE, "malloc_trace");
@@ -46,32 +46,32 @@ static void telemetry_register_workers(void) {
46 worker_register_job_name(WORKER_JOB_ARAL, "aral");
47 }
48
49 -static void telementry_cleanup(void *pptr)
49 +static void pulse_cleanup(void *pptr)
50 {
51 struct netdata_static_thread *static_thread = CLEANUP_FUNCTION_GET_PTR(pptr);
52 if(!static_thread) return;
53
54 static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
55
56 - telemetry_workers_cleanup();
56 + pulse_workers_cleanup();
57 worker_unregister();
58 netdata_log_info("cleaning up...");
59
60 static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
61 }
62
63 -void *telemetry_thread_main(void *ptr) {
64 - CLEANUP_FUNCTION_REGISTER(telementry_cleanup) cleanup_ptr = ptr;
65 - telemetry_register_workers();
63 +void *pulse_thread_main(void *ptr) {
64 + CLEANUP_FUNCTION_REGISTER(pulse_cleanup) cleanup_ptr = ptr;
65 + pulse_register_workers();
66
67 int update_every =
68 - (int)config_get_duration_seconds(CONFIG_SECTION_TELEMETRY, "update every", localhost->rrd_update_every);
68 + (int)config_get_duration_seconds(CONFIG_SECTION_PULSE, "update every", localhost->rrd_update_every);
69 if (update_every < localhost->rrd_update_every) {
70 update_every = localhost->rrd_update_every;
71 - config_set_duration_seconds(CONFIG_SECTION_TELEMETRY, "update every", update_every);
71 + config_set_duration_seconds(CONFIG_SECTION_PULSE, "update every", update_every);
72 }
73
74 - telemerty_aral_init();
74 + pulse_aral_init();
75
76 usec_t step = update_every * USEC_PER_SEC;
77 heartbeat_t hb;
@@ -91,28 +91,28 @@ void *telemetry_thread_main(void *ptr) {
91 }
92 real_step = USEC_PER_SEC;
93
94 - worker_is_busy(WORKER_JOB_TELEMETRY_INGESTION);
95 - telemetry_ingestion_do(telemetry_extended_enabled);
94 + worker_is_busy(WORKER_JOB_INGESTION);
95 + pulse_ingestion_do(pulse_extended_enabled);
96
97 - worker_is_busy(WORKER_JOB_TELEMETRY_HTTP_API);
98 - telemetry_web_do(telemetry_extended_enabled);
97 + worker_is_busy(WORKER_JOB_HTTP_API);
98 + pulse_web_do(pulse_extended_enabled);
99
100 - worker_is_busy(WORKER_JOB_TELEMETRY_QUERIES);
101 - telemetry_queries_do(telemetry_extended_enabled);
100 + worker_is_busy(WORKER_JOB_QUERIES);
101 + pulse_queries_do(pulse_extended_enabled);
102
103 - worker_is_busy(WORKER_JOB_TELEMETRY_ML);
104 - telemetry_ml_do(telemetry_extended_enabled);
103 + worker_is_busy(WORKER_JOB_ML);
104 + pulse_ml_do(pulse_extended_enabled);
105
106 - worker_is_busy(WORKER_JOB_TELEMETRY_GORILLA);
107 - telemetry_gorilla_do(telemetry_extended_enabled);
106 + worker_is_busy(WORKER_JOB_GORILLA);
107 + pulse_gorilla_do(pulse_extended_enabled);
108
109 worker_is_busy(WORKER_JOB_HEARTBEAT);
110 - telemetry_heartbeat_do(telemetry_extended_enabled);
110 + pulse_heartbeat_do(pulse_extended_enabled);
111
112 #ifdef ENABLE_DBENGINE
113 if(dbengine_enabled) {
114 worker_is_busy(WORKER_JOB_DBENGINE);
115 - telemetry_dbengine_do(telemetry_extended_enabled);
115 + pulse_dbengine_do(pulse_extended_enabled);
116 }
117 #endif
118
@@ -120,37 +120,37 @@ void *telemetry_thread_main(void *ptr) {
120 registry_statistics();
121
122 worker_is_busy(WORKER_JOB_STRINGS);
123 - telemetry_string_do(telemetry_extended_enabled);
123 + pulse_string_do(pulse_extended_enabled);
124
125 #ifdef DICT_WITH_STATS
126 worker_is_busy(WORKER_JOB_DICTIONARIES);
127 - telemetry_dictionary_do(telemetry_extended_enabled);
127 + pulse_dictionary_do(pulse_extended_enabled);
128 #endif
129
130 #ifdef NETDATA_TRACE_ALLOCATIONS
131 worker_is_busy(WORKER_JOB_MALLOC_TRACE);
132 - telemetry_trace_allocations_do(telemetry_extended_enabled);
132 + pulse_trace_allocations_do(pulse_extended_enabled);
133 #endif
134
135 worker_is_busy(WORKER_JOB_WORKERS);
136 - telemetry_workers_do(telemetry_extended_enabled);
136 + pulse_workers_do(pulse_extended_enabled);
137
138 worker_is_busy(WORKER_JOB_ARAL);
139 - telemetry_aral_do(telemetry_extended_enabled);
139 + pulse_aral_do(pulse_extended_enabled);
140
141 // keep this last to have access to the memory counters
142 // exposed by everyone else
143 - worker_is_busy(WORKER_JOB_TELEMETRY_DAEMON);
144 - telemetry_daemon_do(telemetry_extended_enabled);
143 + worker_is_busy(WORKER_JOB_DAEMON);
144 + pulse_daemon_do(pulse_extended_enabled);
145 }
146
147 return NULL;
148 }
149
150 // ---------------------------------------------------------------------------------------------------------------------
151 -// telemetry extended thread
151 +// pulse sqlite3 thread
152
153 -static void telemetry_thread_sqlite3_cleanup(void *pptr)
153 +static void pulse_thread_sqlite3_cleanup(void *pptr)
154 {
155 struct netdata_static_thread *static_thread = CLEANUP_FUNCTION_GET_PTR(pptr);
156 if (!static_thread)
@@ -165,15 +165,15 @@ static void telemetry_thread_sqlite3_cleanup(void *pptr)
165 static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
166 }
167
168 -void *telemetry_thread_sqlite3_main(void *ptr) {
169 - CLEANUP_FUNCTION_REGISTER(telemetry_thread_sqlite3_cleanup) cleanup_ptr = ptr;
170 - telemetry_register_workers();
168 +void *pulse_thread_sqlite3_main(void *ptr) {
169 + CLEANUP_FUNCTION_REGISTER(pulse_thread_sqlite3_cleanup) cleanup_ptr = ptr;
170 + pulse_register_workers();
171
172 int update_every =
173 - (int)config_get_duration_seconds(CONFIG_SECTION_TELEMETRY, "update every", localhost->rrd_update_every);
173 + (int)config_get_duration_seconds(CONFIG_SECTION_PULSE, "update every", localhost->rrd_update_every);
174 if (update_every < localhost->rrd_update_every) {
175 update_every = localhost->rrd_update_every;
176 - config_set_duration_seconds(CONFIG_SECTION_TELEMETRY, "update every", update_every);
176 + config_set_duration_seconds(CONFIG_SECTION_PULSE, "update every", update_every);
177 }
178
179 usec_t step = update_every * USEC_PER_SEC;
@@ -191,7 +191,7 @@ void *telemetry_thread_sqlite3_main(void *ptr) {
191 real_step = USEC_PER_SEC;
192
193 worker_is_busy(WORKER_JOB_SQLITE3);
194 - telemetry_sqlite3_do(telemetry_extended_enabled);
194 + pulse_sqlite3_do(pulse_extended_enabled);
195 }
196
197 return NULL;
src/daemon/pulse/pulse.h new
+30
@@ -0,0 +1,30 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PULSE_H
4 +#define NETDATA_PULSE_H 1
5 +
6 +#include "database/rrd.h"
7 +
8 +extern bool pulse_enabled;
9 +extern bool pulse_extended_enabled;
10 +
11 +#include "pulse-http-api.h"
12 +#include "pulse-queries.h"
13 +#include "pulse-ingestion.h"
14 +#include "pulse-ml.h"
15 +#include "pulse-gorilla.h"
16 +#include "pulse-daemon.h"
17 +#include "pulse-daemon-memory.h"
18 +#include "pulse-sqlite3.h"
19 +#include "pulse-dbengine.h"
20 +#include "pulse-string.h"
21 +#include "pulse-heartbeat.h"
22 +#include "pulse-dictionary.h"
23 +#include "pulse-workers.h"
24 +#include "pulse-trace-allocations.h"
25 +#include "pulse-aral.h"
26 +
27 +void *pulse_thread_main(void *ptr);
28 +void *pulse_thread_sqlite3_main(void *ptr);
29 +
30 +#endif /* NETDATA_PULSE_H */
src/daemon/static_threads.c
+10 -10
@@ -12,7 +12,7 @@ void *statsd_main(void *ptr);
12 void *profile_main(void *ptr);
13 void *replication_thread_main(void *ptr);
14
15 -extern bool telemetry_enabled;
15 +extern bool pulse_enabled;
16
17 const struct netdata_static_thread static_threads_common[] = {
18 {
@@ -43,26 +43,26 @@ const struct netdata_static_thread static_threads_common[] = {
43 .start_routine = analytics_main
44 },
45 {
46 - .name = "TELEMETRY",
46 + .name = "PULSE",
47 .config_section = CONFIG_SECTION_PLUGINS,
48 - .config_name = "netdata telemetry",
48 + .config_name = "netdata pulse",
49 .env_name = "NETDATA_INTERNALS_MONITORING",
50 - .global_variable = &telemetry_enabled,
50 + .global_variable = &pulse_enabled,
51 .enabled = 1,
52 .thread = NULL,
53 .init_routine = NULL,
54 - .start_routine = telemetry_thread_main
54 + .start_routine = pulse_thread_main
55 },
56 {
57 - .name = "TLMTRY-SQLITE3",
58 - .config_section = CONFIG_SECTION_TELEMETRY,
59 - .config_name = "extended telemetry",
57 + .name = "PULSE-SQLITE3",
58 + .config_section = CONFIG_SECTION_PULSE,
59 + .config_name = "extended",
60 .env_name = NULL,
61 - .global_variable = &telemetry_extended_enabled,
61 + .global_variable = &pulse_extended_enabled,
62 .enabled = 0, // the default value - it uses netdata.conf for users to enable it
63 .thread = NULL,
64 .init_routine = NULL,
65 - .start_routine = telemetry_thread_sqlite3_main
65 + .start_routine = pulse_thread_sqlite3_main
66 },
67 {
68 .name = "PLUGINSD",
src/daemon/telemetry/telemetry-aral.h deleted
-16
@@ -1,16 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_ARAL_H
4 -#define NETDATA_TELEMETRY_ARAL_H
5 -
6 -#include "daemon/common.h"
7 -
8 -void telemetry_aral_register(ARAL *ar, const char *name);
9 -void telemetry_aral_unregister(ARAL *ar);
10 -
11 -#if defined(TELEMETRY_INTERNALS)
12 -void telemerty_aral_init(void);
13 -void telemetry_aral_do(bool extended);
14 -#endif
15 -
16 -#endif //NETDATA_TELEMETRY_ARAL_H
src/daemon/telemetry/telemetry-daemon.h deleted
-12
@@ -1,12 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_DAEMON_H
4 -#define NETDATA_TELEMETRY_DAEMON_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#if defined(TELEMETRY_INTERNALS)
9 -void telemetry_daemon_do(bool extended);
10 -#endif
11 -
12 -#endif //NETDATA_TELEMETRY_DAEMON_H
src/daemon/telemetry/telemetry-dbengine.h deleted
-17
@@ -1,17 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_DBENGINE_H
4 -#define NETDATA_TELEMETRY_DBENGINE_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#if defined(TELEMETRY_INTERNALS)
9 -extern size_t telemetry_dbengine_total_memory;
10 -
11 -#if defined(ENABLE_DBENGINE)
12 -void telemetry_dbengine_do(bool extended);
13 -#endif
14 -
15 -#endif
16 -
17 -#endif //NETDATA_TELEMETRY_DBENGINE_H
src/daemon/telemetry/telemetry-gorilla.h deleted
-15
@@ -1,15 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_GORILLA_H
4 -#define NETDATA_TELEMETRY_GORILLA_H
5 -
6 -#include "daemon/common.h"
7 -
8 -void telemetry_gorilla_hot_buffer_added();
9 -void telemetry_gorilla_tier0_page_flush(uint32_t actual, uint32_t optimal, uint32_t original);
10 -
11 -#if defined(TELEMETRY_INTERNALS)
12 -void telemetry_gorilla_do(bool extended);
13 -#endif
14 -
15 -#endif //NETDATA_TELEMETRY_GORILLA_H
src/daemon/telemetry/telemetry-heartbeat.h deleted
-12
@@ -1,12 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_HEARTBEAT_H
4 -#define NETDATA_TELEMETRY_HEARTBEAT_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#if defined(TELEMETRY_INTERNALS)
9 -void telemetry_heartbeat_do(bool extended);
10 -#endif
11 -
12 -#endif //NETDATA_TELEMETRY_HEARTBEAT_H
src/daemon/telemetry/telemetry-ingestion.h deleted
-14
@@ -1,14 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_INGESTION_H
4 -#define NETDATA_TELEMETRY_INGESTION_H
5 -
6 -#include "daemon/common.h"
7 -
8 -void telemetry_queries_rrdset_collection_completed(size_t *points_read_per_tier_array);
9 -
10 -#if defined(TELEMETRY_INTERNALS)
11 -void telemetry_ingestion_do(bool extended);
12 -#endif
13 -
14 -#endif //NETDATA_TELEMETRY_INGESTION_H
src/daemon/telemetry/telemetry-ml.h deleted
-33
@@ -1,33 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_ML_H
4 -#define NETDATA_TELEMETRY_ML_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#ifdef __cplusplus
9 -extern "C" {
10 -#endif
11 -
12 -void telemetry_ml_models_consulted(size_t models_consulted);
13 -void telemetry_ml_models_received();
14 -void telemetry_ml_models_ignored();
15 -void telemetry_ml_models_sent();
16 -
17 -void telemetry_ml_memory_allocated(size_t n);
18 -void telemetry_ml_memory_freed(size_t n);
19 -
20 -void global_statistics_ml_models_deserialization_failures();
21 -
22 -uint64_t telemetry_ml_get_current_memory_usage(void);
23 -
24 -#if defined(TELEMETRY_INTERNALS)
25 -void telemetry_ml_do(bool extended);
26 -#endif
27 -
28 -#ifdef __cplusplus
29 -}
30 -#endif
31 -
32 -
33 -#endif //NETDATA_TELEMETRY_ML_H
src/daemon/telemetry/telemetry-queries.h deleted
-17
@@ -1,17 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_QUERIES_H
4 -#define NETDATA_TELEMETRY_QUERIES_H
5 -
6 -#include "daemon/common.h"
7 -
8 -void telemetry_queries_ml_query_completed(size_t points_read);
9 -void telemetry_queries_exporters_query_completed(size_t points_read);
10 -void telemetry_queries_backfill_query_completed(size_t points_read);
11 -void telemetry_queries_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source);
12 -
13 -#if defined(TELEMETRY_INTERNALS)
14 -void telemetry_queries_do(bool extended);
15 -#endif
16 -
17 -#endif //NETDATA_TELEMETRY_QUERIES_H
src/daemon/telemetry/telemetry-sqlite3.h deleted
-15
@@ -1,15 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_SQLITE3_H
4 -#define NETDATA_TELEMETRY_SQLITE3_H
5 -
6 -#include "daemon/common.h"
7 -
8 -void telemetry_sqlite3_query_completed(bool success, bool busy, bool locked);
9 -void telemetry_sqlite3_row_completed(void);
10 -
11 -#if defined(TELEMETRY_INTERNALS)
12 -void telemetry_sqlite3_do(bool extended);
13 -#endif
14 -
15 -#endif //NETDATA_TELEMETRY_SQLITE3_H
src/daemon/telemetry/telemetry-string.h deleted
-12
@@ -1,12 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_STRING_H
4 -#define NETDATA_TELEMETRY_STRING_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#if defined(TELEMETRY_INTERNALS)
9 -void telemetry_string_do(bool extended);
10 -#endif
11 -
12 -#endif //NETDATA_TELEMETRY_STRING_H
src/daemon/telemetry/telemetry-trace-allocations.h deleted
-14
@@ -1,14 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_TRACE_ALLOCATIONS_H
4 -#define NETDATA_TELEMETRY_TRACE_ALLOCATIONS_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#if defined(TELEMETRY_INTERNALS)
9 -#ifdef NETDATA_TRACE_ALLOCATIONS
10 -void telemetry_trace_allocations_do(bool extended);
11 -#endif
12 -#endif
13 -
14 -#endif //NETDATA_TELEMETRY_TRACE_ALLOCATIONS_H
src/daemon/telemetry/telemetry-workers.h deleted
-13
@@ -1,13 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_WORKERS_H
4 -#define NETDATA_TELEMETRY_WORKERS_H
5 -
6 -#include "daemon/common.h"
7 -
8 -#if defined(TELEMETRY_INTERNALS)
9 -void telemetry_workers_do(bool extended);
10 -void telemetry_workers_cleanup(void);
11 -#endif
12 -
13 -#endif //NETDATA_TELEMETRY_WORKERS_H
src/daemon/telemetry/telemetry.h deleted
-30
@@ -1,30 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_TELEMETRY_H
4 -#define NETDATA_TELEMETRY_H 1
5 -
6 -#include "database/rrd.h"
7 -
8 -extern bool telemetry_enabled;
9 -extern bool telemetry_extended_enabled;
10 -
11 -#include "telemetry-http-api.h"
12 -#include "telemetry-queries.h"
13 -#include "telemetry-ingestion.h"
14 -#include "telemetry-ml.h"
15 -#include "telemetry-gorilla.h"
16 -#include "telemetry-daemon.h"
17 -#include "telemetry-daemon-memory.h"
18 -#include "telemetry-sqlite3.h"
19 -#include "telemetry-dbengine.h"
20 -#include "telemetry-string.h"
21 -#include "telemetry-heartbeat.h"
22 -#include "telemetry-dictionary.h"
23 -#include "telemetry-workers.h"
24 -#include "telemetry-trace-allocations.h"
25 -#include "telemetry-aral.h"
26 -
27 -void *telemetry_thread_main(void *ptr);
28 -void *telemetry_thread_sqlite3_main(void *ptr);
29 -
30 -#endif /* NETDATA_TELEMETRY_H */
src/database/contexts/query_target.c
+2 -2
@@ -27,12 +27,12 @@ static struct {
27 } used;
28 } query_target_base = {
29 .available = {
30 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
30 + .spinlock = SPINLOCK_INITIALIZER,
31 .base = NULL,
32 .count = 0,
33 },
34 .used = {
35 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
35 + .spinlock = SPINLOCK_INITIALIZER,
36 .base = NULL,
37 .count = 0,
38 },
src/database/engine/cache.c
+58 -79
@@ -121,11 +121,10 @@ struct pgc {
121 struct pgc_index {
122 alignas(64) RW_SPINLOCK rw_spinlock;
123 Pvoid_t sections_judy;
124 - } *index;
125 -
124 #ifdef PGC_WITH_ARAL
127 - ARAL *aral;
125 + ARAL *aral;
126 #endif
127 + } *index;
128
129 struct {
130 alignas(64) SPINLOCK spinlock;
@@ -399,6 +398,7 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
398 if(current_cache_size > wanted_cache_size && wanted_cache_size < current_cache_size - clean)
399 wanted_cache_size = current_cache_size - clean;
400
401 + bool signal_the_evictor = false;
402 if(cache->config.out_of_memory_protection_bytes) {
403 // out of memory protection
404 OS_SYSTEM_MEMORY sm = os_system_memory(false);
@@ -409,6 +409,7 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
409 if (sm.ram_available_bytes < min_available) {
410 // we must shrink
411 wanted_cache_size = current_cache_size - (min_available - sm.ram_available_bytes);
412 + signal_the_evictor = true;
413 }
414 else if(cache->config.use_all_ram) {
415 // we can grow
@@ -443,6 +444,12 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
444 else if(per1000 >= cache->config.aggressive_evict_per1000)
445 __atomic_add_fetch(&cache->stats.events_cache_needs_space_aggressively, 1, __ATOMIC_RELAXED);
446
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);
451 + }
452 +
453 return per1000;
454 }
455
@@ -464,12 +471,6 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
471 static void signal_evict_thread_or_evict_inline(PGC *cache, bool on_release) {
472 const size_t per1000 = cache_usage_per1000(cache, NULL);
473
467 - if (per1000 >= cache->config.healthy_size_per1000 && spinlock_trylock(&cache->evictor.spinlock)) {
468 - __atomic_add_fetch(&cache->stats.waste_evict_thread_signals, 1, __ATOMIC_RELAXED);
469 - completion_mark_complete_a_job(&cache->evictor.completion);
470 - spinlock_unlock(&cache->evictor.spinlock);
471 - }
472 -
474 if(!(cache->config.options & PGC_OPTIONS_EVICT_PAGES_NO_INLINE)) {
475 if (per1000 > cache->config.aggressive_evict_per1000 && !on_release) {
476 // the threads that add pages, turn into evictors when the cache needs evictions aggressively
@@ -560,26 +561,15 @@ struct section_pages {
561 static struct aral_statistics aral_statistics_for_pgc = { 0 };
562
563 static ARAL *pgc_sections_aral = NULL;
563 -static ARAL *pgc_pages_aral = NULL;
564
565 static void pgc_section_pages_static_aral_init(void) {
566 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
566 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
567
568 spinlock_lock(&spinlock);
569
570 if(!pgc_sections_aral)
571 pgc_sections_aral = aral_by_size_acquire(sizeof(struct section_pages));
572
573 - if(!pgc_pages_aral) {
574 - pgc_pages_aral = aral_create(
575 - "pgc_pages",
576 - sizeof(PGC_PAGE),
577 - 0,
578 - 0,
579 - &aral_statistics_for_pgc,
580 - NULL, NULL, false, false);
581 - }
582 -
573 spinlock_unlock(&spinlock);
574 }
575
@@ -646,14 +636,12 @@ static void pgc_queue_add(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PA
636 // - New pages created as CLEAN, always have 1 access.
637 // - DIRTY pages made CLEAN, depending on their accesses may be appended (accesses > 0) or prepended (accesses = 0).
638
649 - // FIXME - is it better for fragmentation to always append?
650 -
651 -// if(page->accesses || page_flag_check(page, PGC_PAGE_HAS_BEEN_ACCESSED | PGC_PAGE_HAS_NO_DATA_IGNORE_ACCESSES) == PGC_PAGE_HAS_BEEN_ACCESSED) {
639 + if(page->accesses || page_flag_check(page, PGC_PAGE_HAS_BEEN_ACCESSED | PGC_PAGE_HAS_NO_DATA_IGNORE_ACCESSES) == PGC_PAGE_HAS_BEEN_ACCESSED) {
640 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(q->base, page, link.prev, link.next);
641 page_flag_clear(page, PGC_PAGE_HAS_BEEN_ACCESSED);
654 -// }
655 -// else
656 -// DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(q->base, page, link.prev, link.next);
642 + }
643 + else
644 + DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(q->base, page, link.prev, link.next);
645
646 q->version++;
647 }
@@ -1037,7 +1025,7 @@ static inline void free_this_page(PGC *cache, PGC_PAGE *page, size_t partition _
1025
1026 // free our memory
1027 #ifdef PGC_WITH_ARAL
1040 - aral_freez(cache->aral, page);
1028 + aral_freez(cache->index[partition].aral, page);
1029 #else
1030 freez(page);
1031 #endif
@@ -1431,7 +1419,7 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1419 size_t partition = pgc_indexing_partition(cache, entry->metric_id);
1420
1421 #ifdef PGC_WITH_ARAL
1434 - PGC_PAGE *allocation = aral_mallocz(cache->aral);
1422 + PGC_PAGE *allocation = aral_mallocz(cache->index[partition].aral);
1423 #endif
1424 PGC_PAGE *page;
1425 size_t spins = 0;
@@ -1534,7 +1522,7 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1522 if(unlikely(!page)) {
1523 // now that we don't have the lock,
1524 // give it some time for the old page to go away
1537 - yield_the_processor();
1525 + tinysleep();
1526 }
1527 }
1528
@@ -1542,7 +1530,7 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1530
1531 #ifdef PGC_WITH_ARAL
1532 if(allocation)
1545 - aral_freez(cache->aral, allocation);
1533 + aral_freez(cache->index[partition].aral, allocation);
1534 #endif
1535
1536 __atomic_sub_fetch(&cache->stats.workers_add, 1, __ATOMIC_RELAXED);
@@ -1950,7 +1938,9 @@ static void *pgc_evict_thread(void *ptr) {
1938
1939 while (true) {
1940 worker_is_idle();
1953 - unsigned new_job_id = completion_wait_for_a_job_with_timeout(&cache->evictor.completion, job_id, 50);
1941 + unsigned new_job_id = completion_wait_for_a_job_with_timeout(
1942 + &cache->evictor.completion, job_id, 100);
1943 +
1944 bool was_signaled = new_job_id > job_id;
1945 worker_is_busy(was_signaled ? 1 : 0);
1946 job_id = new_job_id;
@@ -1958,31 +1948,16 @@ static void *pgc_evict_thread(void *ptr) {
1948 if (nd_thread_signaled_to_cancel())
1949 return NULL;
1950
1961 - spinlock_lock(&cache->evictor.spinlock);
1962 -
1963 - size_t at_once = 10;
1951 size_t size_to_evict = 0;
1952 size_t per1000 = cache_usage_per1000(cache, &size_to_evict);
1966 - bool was_aggressive = per1000 > cache->config.aggressive_evict_per1000;
1967 -
1968 - while (size_to_evict && ((--at_once && size_to_evict && per1000 > cache->config.healthy_size_per1000) || (per1000 > cache->config.aggressive_evict_per1000))) {
1969 - if (nd_thread_signaled_to_cancel()) {
1970 - spinlock_unlock(&cache->evictor.spinlock);
1971 - return NULL;
1972 - }
1953 + bool was_critical = per1000 >= cache->config.severe_pressure_per1000;
1954
1955 + if(size_to_evict > 0) {
1956 evict_pages(cache, 0, 0, true, false);
1957
1976 - if(was_signaled || was_aggressive)
1958 + if (was_signaled || was_critical)
1959 mallocz_release_as_much_memory_to_the_system();
1978 -
1979 - yield_the_processor();
1980 -
1981 - size_to_evict = 0;
1982 - per1000 = cache_usage_per1000(cache, &size_to_evict);
1960 }
1984 -
1985 - spinlock_unlock(&cache->evictor.spinlock);
1961 }
1962
1963 worker_unregister();
@@ -2020,7 +1995,7 @@ PGC *pgc_create(const char *name,
1995
1996 cache->config.options = options;
1997 cache->config.additional_bytes_per_page = additional_bytes_per_page;
2023 - cache->config.stats = telemetry_enabled;
1998 + cache->config.stats = pulse_enabled;
1999
2000 // flushing
2001 cache->config.max_flushes_inline = (max_flushes_inline == 0) ? 2 : max_flushes_inline;
@@ -2035,39 +2010,45 @@ PGC *pgc_create(const char *name,
2010 cache->config.max_pages_per_inline_eviction = max_pages_per_inline_eviction;
2011 cache->config.max_skip_pages_per_inline_eviction = (max_skip_pages_per_inline_eviction < 2) ? 2 : max_skip_pages_per_inline_eviction;
2012 cache->config.severe_pressure_per1000 = 1010; // INLINE: use releasers to evict pages (up to max_pages_per_inline_eviction)
2038 - cache->config.aggressive_evict_per1000 = 990; // INLINE: use adders to evict page (up to max_pages_per_inline_eviction)
2039 - cache->config.healthy_size_per1000 = 980; // signal the eviction thread to evict immediately
2013 + cache->config.aggressive_evict_per1000 = 990; // INLINE: use adders to evict pages (up to max_pages_per_inline_eviction)
2014 + cache->config.healthy_size_per1000 = 980; // no evictions happen below this threshold
2015 cache->config.evict_low_threshold_per1000 = 970; // when evicting, bring the size down to this threshold
2016 + // the eviction thread is signaled ONLY if we run out of memory
2017 + // otherwise, it runs by itself every 100ms
2018
2019 // use all ram and protection from out of memory
2020 cache->config.use_all_ram = dbengine_use_all_ram_for_caches;
2021 cache->config.out_of_memory_protection_bytes = dbengine_out_of_memory_protection;
2022
2023 // partitions
2047 - cache->config.partitions = partitions == 0 ? 1ULL + get_netdata_cpus() / 2 : partitions;
2024 + if(partitions == 0) partitions = get_netdata_cpus();
2025 + if(partitions <= 4) partitions = 4;
2026 + if(partitions > 256) partitions = 256;
2027 + cache->config.partitions = partitions;
2028 cache->index = callocz(cache->config.partitions, sizeof(struct pgc_index));
2029
2030 pgc_section_pages_static_aral_init();
2031
2052 - for(size_t part = 0; part < cache->config.partitions ; part++)
2032 + for(size_t part = 0; part < cache->config.partitions ; part++) {
2033 rw_spinlock_init(&cache->index[part].rw_spinlock);
2054 -
2034 #ifdef PGC_WITH_ARAL
2056 - if(cache->config.additional_bytes_per_page) {
2057 - char buf[100];
2058 - snprintfz(buf, sizeof(buf), "%s", name);
2059 - cache->aral = aral_create(
2060 - buf,
2061 - sizeof(PGC_PAGE) + cache->config.additional_bytes_per_page,
2062 - 0,
2063 - 16364,
2064 - &aral_statistics_for_pgc,
2065 - NULL, NULL, false, false);
2035 + {
2036 + char buf[100];
2037 + snprintfz(buf, sizeof(buf), "%s", name);
2038 + cache->index[part].aral = aral_create(
2039 + buf,
2040 + sizeof(PGC_PAGE) + cache->config.additional_bytes_per_page,
2041 + 0,
2042 + 0,
2043 + &aral_statistics_for_pgc,
2044 + NULL,
2045 + NULL,
2046 + false,
2047 + false);
2048 + }
2049 }
2067 - else
2068 - cache->aral = pgc_pages_aral;
2050
2070 - telemetry_aral_register(cache->aral, "pgc");
2051 + pulse_aral_register(cache->index[0].aral, "pgc");
2052 #endif
2053
2054
@@ -2103,11 +2084,11 @@ PGC *pgc_create(const char *name,
2084 }
2085
2086 size_t pgc_aral_structures(void) {
2106 - return aral_structures(pgc_pages_aral);
2087 + return aral_structures_from_stats(&aral_statistics_for_pgc);
2088 }
2089
2090 size_t pgc_aral_overhead(void) {
2110 - return aral_overhead(pgc_pages_aral);
2091 + return aral_overhead_from_stats(&aral_statistics_for_pgc);
2092 }
2093
2094 void pgc_flush_all_hot_and_dirty_pages(PGC *cache, Word_t section) {
@@ -2138,13 +2119,13 @@ void pgc_destroy(PGC *cache) {
2119 else {
2120 pointer_destroy_index(cache);
2121
2141 -// for(size_t part = 0; part < cache->config.partitions ; part++)
2142 -// netdata_rwlock_destroy(&cache->index[part].rw_spinlock);
2143 -
2122 + for(size_t part = 0; part < cache->config.partitions ;part++) {
2123 + // netdata_rwlock_destroy(&cache->index[part].rw_spinlock);
2124 #ifdef PGC_WITH_ARAL
2145 - if(cache->config.additional_bytes_per_page)
2146 - aral_destroy(cache->aral);
2125 + aral_destroy(cache->index[part].aral);
2126 #endif
2127 + }
2128 +
2129 freez(cache->index);
2130 freez(cache);
2131 }
@@ -2300,12 +2281,10 @@ void pgc_set_nominal_page_size_callback(PGC *cache, nominal_page_size_callback c
2281 }
2282
2283 size_t pgc_get_current_cache_size(PGC *cache) {
2303 - cache_usage_per1000(cache, NULL);
2284 return __atomic_load_n(&cache->stats.current_cache_size, __ATOMIC_RELAXED);
2285 }
2286
2287 size_t pgc_get_wanted_cache_size(PGC *cache) {
2308 - cache_usage_per1000(cache, NULL);
2288 return __atomic_load_n(&cache->stats.wanted_cache_size, __ATOMIC_RELAXED);
2289 }
2290
@@ -2397,7 +2376,7 @@ PGC_PAGE *pgc_page_get_and_acquire(PGC *cache, Word_t section, Word_t metric_id,
2376 if(page || !retry)
2377 break;
2378
2400 - yield_the_processor();
2379 + tinysleep();
2380 }
2381
2382 if(page) {
src/database/engine/datafile.c
+3 -2
@@ -56,10 +56,11 @@ bool datafile_acquire(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS re
56 return ret;
57 }
58
59 -void datafile_release(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS reason) {
59 +void datafile_release_with_trace(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS reason, const char *func) {
60 spinlock_lock(&df->users.spinlock);
61 if(!df->users.lockers)
62 - fatal("DBENGINE DATAFILE: cannot release a datafile that is not acquired");
62 + fatal("DBENGINE DATAFILE: cannot release datafile %u of tier %u - it is not acquired, called from %s() with reason %u",
63 + df->fileno, df->tier, func, reason);
64
65 df->users.lockers--;
66 df->users.lockers_by_reason[reason]--;
src/database/engine/datafile.h
+2 -1
@@ -76,7 +76,8 @@ struct rrdengine_datafile {
76 };
77
78 bool datafile_acquire(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS reason);
79 -void datafile_release(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS reason);
79 +void datafile_release_with_trace(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS reason, const char *func);
80 +#define datafile_release(df, reason) datafile_release_with_trace(df, reason, __FUNCTION__)
81 bool datafile_acquire_for_deletion(struct rrdengine_datafile *df, bool is_shutdown);
82
83 void datafile_list_insert(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, bool having_lock);
src/database/engine/metric.c
+2 -2
@@ -375,7 +375,7 @@ inline MRG *mrg_create(ssize_t partitions) {
375
376 mrg->index[i].aral = aral_create(buf, sizeof(METRIC), 0, 16384, &mrg_aral_statistics, NULL, NULL, false, false);
377 }
378 - telemetry_aral_register(mrg->index[0].aral, "mrg");
378 + pulse_aral_register(mrg->index[0].aral, "mrg");
379
380 return mrg;
381 }
@@ -395,7 +395,7 @@ inline void mrg_destroy(MRG *mrg __maybe_unused) {
395 // to delete entries, the caller needs to keep pointers to them
396 // and delete them one by one
397
398 - telemetry_aral_unregister(mrg->index[0].aral);
398 + pulse_aral_unregister(mrg->index[0].aral);
399 }
400
401 inline METRIC *mrg_metric_add_and_acquire(MRG *mrg, MRG_ENTRY entry, bool *ret) {
src/database/engine/page.c
+30 -19
@@ -54,18 +54,24 @@ struct pgd {
54 // ----------------------------------------------------------------------------
55 // memory management
56
57 -#define ARAL_TOLERANCE_TO_DEDUP 7 // deduplicate aral sizes, if the delta is below this number of bytes
58 -#define PGD_ARAL_PARTITIONS 4
57 +// deduplicate aral sizes, if the delta is below this number of bytes
58 +#define ARAL_TOLERANCE_TO_DEDUP 7
59 +
60 +// max, we use as many as the cpu cores
61 +// cannot be bigger than 256, due to struct pgd->partition (uint8_t)
62 +#define PGD_ARAL_PARTITIONS_MAX 256
63
64 struct {
65 + size_t partitions;
66 +
67 size_t sizeof_pgd;
68 size_t sizeof_gorilla_writer_t;
69 size_t sizeof_gorilla_buffer_32bit;
70
65 - ARAL *aral_pgd[PGD_ARAL_PARTITIONS];
66 - ARAL *aral_gorilla_buffer[PGD_ARAL_PARTITIONS];
67 - ARAL *aral_gorilla_writer[PGD_ARAL_PARTITIONS];
68 -} pgd_alloc_globals = {};
71 + ARAL *aral_pgd[PGD_ARAL_PARTITIONS_MAX];
72 + ARAL *aral_gorilla_buffer[PGD_ARAL_PARTITIONS_MAX];
73 + ARAL *aral_gorilla_writer[PGD_ARAL_PARTITIONS_MAX];
74 +} pgd_alloc_globals = { 0 };
75
76 #if RRD_STORAGE_TIERS != 5
77 #error "You need to update the slots reserved for storage tiers"
@@ -110,6 +116,11 @@ int aral_size_sort_compare(const void *a, const void *b) {
116 }
117
118 void pgd_init_arals(void) {
119 + size_t partitions = get_netdata_cpus();
120 + if(partitions < 4) partitions = 4;
121 + if(partitions > PGD_ARAL_PARTITIONS_MAX) partitions = PGD_ARAL_PARTITIONS_MAX;
122 + pgd_alloc_globals.partitions = partitions;
123 +
124 aral_sizes_count = _countof(aral_sizes);
125
126 for(size_t i = 0; i < RRD_STORAGE_TIERS ;i++)
@@ -145,9 +156,9 @@ void pgd_init_arals(void) {
156 aral_sizes[i] = 0;
157
158 // allocate all the arals
148 - arals = callocz(aral_sizes_count * PGD_ARAL_PARTITIONS, sizeof(ARAL *));
159 + arals = callocz(aral_sizes_count * pgd_alloc_globals.partitions, sizeof(ARAL *));
160 for(size_t slot = 0; slot < aral_sizes_count ; slot++) {
150 - for(size_t partition = 0; partition < PGD_ARAL_PARTITIONS; partition++) {
161 + for(size_t partition = 0; partition < pgd_alloc_globals.partitions; partition++) {
162
163 if(partition > 0 && aral_sizes[slot] > 128) {
164 // do not create partitions for sizes above 128 bytes
@@ -169,7 +180,7 @@ void pgd_init_arals(void) {
180 }
181 }
182
172 - for(size_t p = 0; p < PGD_ARAL_PARTITIONS ;p++) {
183 + for(size_t p = 0; p < pgd_alloc_globals.partitions ;p++) {
184 pgd_alloc_globals.aral_pgd[p] = pgd_get_aral_by_size_and_partition(sizeof(PGD), p);
185 pgd_alloc_globals.aral_gorilla_writer[p] = pgd_get_aral_by_size_and_partition(sizeof(gorilla_writer_t), p);
186 pgd_alloc_globals.aral_gorilla_buffer[p] = pgd_get_aral_by_size_and_partition(RRDENG_GORILLA_32BIT_BUFFER_SIZE, p);
@@ -184,11 +195,11 @@ void pgd_init_arals(void) {
195 pgd_alloc_globals.sizeof_gorilla_writer_t = aral_actual_element_size(pgd_alloc_globals.aral_gorilla_writer[0]);
196 pgd_alloc_globals.sizeof_gorilla_buffer_32bit = aral_actual_element_size(pgd_alloc_globals.aral_gorilla_buffer[0]);
197
187 - telemetry_aral_register(pgd_alloc_globals.aral_pgd[0], "pgd");
198 + pulse_aral_register(pgd_alloc_globals.aral_pgd[0], "pgd");
199 }
200
201 static ARAL *pgd_get_aral_by_size_and_partition(size_t size, size_t partition) {
191 - internal_fatal(partition >= PGD_ARAL_PARTITIONS, "Wrong partition %zu", partition);
202 + internal_fatal(partition >= pgd_alloc_globals.partitions, "Wrong partition %zu", partition);
203
204 size_t slot;
205
@@ -218,17 +229,17 @@ static ARAL *pgd_get_aral_by_size_and_partition(size_t size, size_t partition) {
229 }
230
231 static inline gorilla_writer_t *pgd_gorilla_writer_alloc(size_t partition) {
221 - internal_fatal(partition >= PGD_ARAL_PARTITIONS, "invalid gorilla writer partition %zu", partition);
232 + internal_fatal(partition >= pgd_alloc_globals.partitions, "invalid gorilla writer partition %zu", partition);
233 return aral_mallocz_marked(pgd_alloc_globals.aral_gorilla_writer[partition]);
234 }
235
236 static inline gorilla_buffer_t *pgd_gorilla_buffer_alloc(size_t partition) {
226 - internal_fatal(partition >= PGD_ARAL_PARTITIONS, "invalid gorilla buffer partition %zu", partition);
237 + internal_fatal(partition >= pgd_alloc_globals.partitions, "invalid gorilla buffer partition %zu", partition);
238 return aral_mallocz_marked(pgd_alloc_globals.aral_gorilla_buffer[partition]);
239 }
240
241 static inline PGD *pgd_alloc(bool for_collector) {
231 - size_t partition = gettid_cached() % PGD_ARAL_PARTITIONS;
242 + size_t partition = gettid_cached() % pgd_alloc_globals.partitions;
243 PGD *pgd;
244
245 if(for_collector)
@@ -312,7 +323,7 @@ PGD *pgd_create(uint8_t type, uint32_t slots) {
323 // allocate new gorilla buffer
324 gorilla_buffer_t *gbuf = pgd_gorilla_buffer_alloc(pg->partition);
325 memset(gbuf, 0, RRDENG_GORILLA_32BIT_BUFFER_SIZE);
315 - telemetry_gorilla_hot_buffer_added();
326 + pulse_gorilla_hot_buffer_added();
327
328 *pg->gorilla.writer = gorilla_writer_init(gbuf, RRDENG_GORILLA_32BIT_BUFFER_SLOTS);
329 pg->gorilla.num_buffers = 1;
@@ -381,7 +392,7 @@ void pgd_free(PGD *pg) {
392 if (!pg || pg == PGD_EMPTY)
393 return;
394
384 - internal_fatal(pg->partition >= PGD_ARAL_PARTITIONS,
395 + internal_fatal(pg->partition >= pgd_alloc_globals.partitions,
396 "PGD partition is invalid %u", pg->partition);
397
398 switch (pg->type)
@@ -457,7 +468,7 @@ static void pgd_aral_unmark(PGD *pg) {
468 !(pg->options & PAGE_OPTION_ARAL_MARKED))
469 return;
470
460 - internal_fatal(pg->partition >= PGD_ARAL_PARTITIONS,
471 + internal_fatal(pg->partition >= pgd_alloc_globals.partitions,
472 "PGD partition is invalid %u", pg->partition);
473
474 switch (pg->type)
@@ -650,7 +661,7 @@ uint32_t pgd_disk_footprint(PGD *pg)
661 size = pg->gorilla.num_buffers * RRDENG_GORILLA_32BIT_BUFFER_SIZE;
662
663 if (pg->states & PGD_STATE_CREATED_FROM_COLLECTOR)
653 - telemetry_gorilla_tier0_page_flush(
664 + pulse_gorilla_tier0_page_flush(
665 gorilla_writer_actual_nbytes(pg->gorilla.writer),
666 gorilla_writer_optimal_nbytes(pg->gorilla.writer),
667 tier_page_size[0]);
@@ -778,7 +789,7 @@ size_t pgd_append_point(PGD *pg,
789
790 gorilla_writer_add_buffer(pg->gorilla.writer, new_buffer, RRDENG_GORILLA_32BIT_BUFFER_SLOTS);
791 pg->gorilla.num_buffers += 1;
781 - telemetry_gorilla_hot_buffer_added();
792 + pulse_gorilla_hot_buffer_added();
793
794 ok = gorilla_writer_write(pg->gorilla.writer, t);
795 internal_fatal(ok == false, "Failed to writer value in newly allocated gorilla buffer.");
src/database/engine/pagecache.c
+13 -30
@@ -1033,49 +1033,32 @@ 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 * 5; // 5%
1037 -
1038 -// static bool query_current_size = true;
1039 -// if(query_current_size) {
1040 -// size_t main_current_cache_size = pgc_get_current_cache_size(main_cache);
1041 -//
1042 -// size_t main_free_cache_size = (main_wanted_cache_size > main_current_cache_size) ?
1043 -// main_wanted_cache_size - main_current_cache_size : 0;
1044 -//
1045 -// if(main_free_cache_size > target_size)
1046 -// target_size = main_free_cache_size;
1047 -// else
1048 -// query_current_size = false;
1049 -// }
1036 + size_t target_size = main_wanted_cache_size / 100 * 10; // 10%
1037
1038 if(target_size < 2 * 1024 * 1024)
1039 target_size = 2 * 1024 * 1024;
1040
1054 - return target_size;
1041 + size_t main_current_cache_size = pgc_get_current_cache_size(main_cache);
1042 +
1043 + size_t main_free_cache_size = (main_wanted_cache_size > main_current_cache_size) ?
1044 + main_wanted_cache_size - main_current_cache_size : 0;
1045 +
1046 + return target_size + main_free_cache_size;
1047 }
1048
1049 size_t dynamic_extent_cache_size(void) {
1050 size_t main_wanted_cache_size = pgc_get_wanted_cache_size(main_cache);
1059 -
1051 size_t target_size = main_wanted_cache_size / 100 * 10; // 10%
1052
1062 -// static bool query_current_size = true;
1063 -// if(query_current_size) {
1064 -// size_t main_current_cache_size = pgc_get_current_cache_size(main_cache);
1065 -//
1066 -// size_t main_free_cache_size = (main_wanted_cache_size > main_current_cache_size) ?
1067 -// main_wanted_cache_size - main_current_cache_size : 0;
1068 -//
1069 -// if(main_free_cache_size > target_size)
1070 -// target_size = main_free_cache_size;
1071 -// else
1072 -// query_current_size = false;
1073 -// }
1074 -
1053 if(target_size < 5 * 1024 * 1024)
1054 target_size = 5 * 1024 * 1024;
1055
1078 - return target_size;
1056 + size_t main_current_cache_size = pgc_get_current_cache_size(main_cache);
1057 +
1058 + size_t main_free_cache_size = (main_wanted_cache_size > main_current_cache_size) ?
1059 + main_wanted_cache_size - main_current_cache_size : 0;
1060 +
1061 + return target_size + main_free_cache_size;
1062 }
1063
1064 size_t pgc_main_nominal_page_size(void *data) {
src/database/engine/pdc.c
+5 -5
@@ -58,7 +58,7 @@ void pdc_init(void) {
58 NULL, NULL, false, false
59 );
60
61 - telemetry_aral_register(pdc_globals.pdc.ar, "pdc");
61 + pulse_aral_register(pdc_globals.pdc.ar, "pdc");
62 }
63
64 PDC *pdc_get(void) {
@@ -87,7 +87,7 @@ void page_details_init(void) {
87 NULL,
88 NULL, NULL, false, false
89 );
90 - telemetry_aral_register(pdc_globals.pd.ar, "pd");
90 + pulse_aral_register(pdc_globals.pd.ar, "pd");
91 }
92
93 struct page_details *page_details_get(void) {
@@ -116,7 +116,7 @@ void epdl_init(void) {
116 NULL,
117 NULL, NULL, false, false
118 );
119 - telemetry_aral_register(pdc_globals.epdl.ar, "epdl");
119 + pulse_aral_register(pdc_globals.epdl.ar, "epdl");
120 }
121
122 static EPDL *epdl_get(void) {
@@ -146,7 +146,7 @@ void deol_init(void) {
146 NULL, NULL, false, false
147 );
148
149 - telemetry_aral_register(pdc_globals.deol.ar, "deol");
149 + pulse_aral_register(pdc_globals.deol.ar, "deol");
150 }
151
152 static DEOL *deol_get(void) {
@@ -182,7 +182,7 @@ static struct {
182
183 } extent_buffer_globals = {
184 .protected = {
185 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
185 + .spinlock = SPINLOCK_INITIALIZER,
186 .available_items = NULL,
187 .available = 0,
188 },
src/database/engine/rrdengine.c
+8 -8
@@ -95,7 +95,7 @@ struct rrdeng_main {
95
96 .cmd_queue = {
97 .unsafe = {
98 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
98 + .spinlock = SPINLOCK_INITIALIZER,
99 },
100 }
101 };
@@ -149,7 +149,7 @@ static void work_request_init(void) {
149 NULL, NULL, false, false
150 );
151
152 - telemetry_aral_register(rrdeng_main.work_cmd.ar, "workers");
152 + pulse_aral_register(rrdeng_main.work_cmd.ar, "workers");
153 }
154
155 enum LIBUV_WORKERS_STATUS {
@@ -269,7 +269,7 @@ void page_descriptors_init(void) {
269 NULL,
270 NULL, NULL, false, false);
271
272 - telemetry_aral_register(rrdeng_main.xt_io_descr.ar, "descriptors");
272 + pulse_aral_register(rrdeng_main.xt_io_descr.ar, "descriptors");
273 }
274
275 struct page_descr_with_data *page_descriptor_get(void) {
@@ -295,7 +295,7 @@ static void extent_io_descriptor_init(void) {
295 NULL, NULL, false, false
296 );
297
298 - telemetry_aral_register(rrdeng_main.xt_io_descr.ar, "extent io");
298 + pulse_aral_register(rrdeng_main.xt_io_descr.ar, "extent io");
299 }
300
301 static struct extent_io_descriptor *extent_io_descriptor_get(void) {
@@ -320,7 +320,7 @@ void rrdeng_query_handle_init(void) {
320 NULL,
321 NULL, NULL, false, false);
322
323 - telemetry_aral_register(rrdeng_main.handles.ar, "query handles");
323 + pulse_aral_register(rrdeng_main.handles.ar, "query handles");
324 }
325
326 struct rrdeng_query_handle *rrdeng_query_handle_get(void) {
@@ -348,7 +348,7 @@ static struct {
348 } atomics;
349 } wal_globals = {
350 .protected = {
351 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
351 + .spinlock = SPINLOCK_INITIALIZER,
352 .available_items = NULL,
353 .available = 0,
354 },
@@ -442,7 +442,7 @@ static void rrdeng_cmd_queue_init(void) {
442 NULL,
443 NULL, NULL, false, false);
444
445 - telemetry_aral_register(rrdeng_main.cmd_queue.ar, "opcodes");
445 + pulse_aral_register(rrdeng_main.cmd_queue.ar, "opcodes");
446 }
447
448 static inline STORAGE_PRIORITY rrdeng_enq_cmd_map_opcode_to_priority(enum rrdeng_opcode opcode, STORAGE_PRIORITY priority) {
@@ -1745,7 +1745,7 @@ static void dbengine_initialize_structures(void) {
1745
1746 bool rrdeng_dbengine_spawn(struct rrdengine_instance *ctx __maybe_unused) {
1747 static bool spawned = false;
1748 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
1748 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
1749
1750 spinlock_lock(&spinlock);
1751
src/database/engine/rrdengineapi.c
+1 -1
@@ -692,7 +692,7 @@ void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *sch
692 // query ops
693
694 #ifdef NETDATA_INTERNAL_CHECKS
695 -SPINLOCK global_query_handle_spinlock = NETDATA_SPINLOCK_INITIALIZER;
695 +SPINLOCK global_query_handle_spinlock = SPINLOCK_INITIALIZER;
696 static struct rrdeng_query_handle *global_query_handle_ll = NULL;
697 static void register_query_handle(struct rrdeng_query_handle *handle) {
698 handle->query_pid = gettid_cached();
src/database/rrdlabels.c
+1 -2
@@ -10,8 +10,7 @@ struct {
10 SPINLOCK spinlock;
11 } global_labels = {
12 .JudyHS = (Pvoid_t) NULL,
13 - .spinlock = NETDATA_SPINLOCK_INITIALIZER
14 -};
13 + .spinlock = SPINLOCK_INITIALIZER};
14
15 typedef struct label_registry_idx {
16 STRING *key;
src/database/rrdset.c
+1 -1
@@ -1293,7 +1293,7 @@ void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n,
1293 }
1294
1295 void store_metric_collection_completed() {
1296 - telemetry_queries_rrdset_collection_completed(rrdset_done_statistics_points_stored_per_tier);
1296 + pulse_queries_rrdset_collection_completed(rrdset_done_statistics_points_stored_per_tier);
1297 }
1298
1299 // caching of dimensions rrdset_done() and rrdset_done_interpolate() loop through
src/database/sqlite/sqlite_functions.c
+5 -5
@@ -13,7 +13,7 @@ SQLITE_API int sqlite3_exec_monitored(
13 char **errmsg /* Error msg written here */
14 ) {
15 int rc = sqlite3_exec(db, sql, callback, data, errmsg);
16 - telemetry_sqlite3_query_completed(rc == SQLITE_OK, rc == SQLITE_BUSY, rc == SQLITE_LOCKED);
16 + pulse_sqlite3_query_completed(rc == SQLITE_OK, rc == SQLITE_BUSY, rc == SQLITE_LOCKED);
17 return rc;
18 }
19
@@ -25,14 +25,14 @@ SQLITE_API int sqlite3_step_monitored(sqlite3_stmt *stmt) {
25 rc = sqlite3_step(stmt);
26 switch (rc) {
27 case SQLITE_DONE:
28 - telemetry_sqlite3_query_completed(1, 0, 0);
28 + pulse_sqlite3_query_completed(1, 0, 0);
29 break;
30 case SQLITE_ROW:
31 - telemetry_sqlite3_row_completed();
31 + pulse_sqlite3_row_completed();
32 break;
33 case SQLITE_BUSY:
34 case SQLITE_LOCKED:
35 - telemetry_sqlite3_query_completed(false, rc == SQLITE_BUSY, rc == SQLITE_LOCKED);
35 + pulse_sqlite3_query_completed(false, rc == SQLITE_BUSY, rc == SQLITE_LOCKED);
36 usleep(SQLITE_INSERT_DELAY * USEC_PER_MS);
37 continue;
38 default:
@@ -402,7 +402,7 @@ int sqlite_library_init(void)
402 return (SQLITE_OK != rc);
403 }
404
405 -SPINLOCK sqlite_spinlock = NETDATA_SPINLOCK_INITIALIZER;
405 +SPINLOCK sqlite_spinlock = SPINLOCK_INITIALIZER;
406
407 void sqlite_library_shutdown(void)
408 {
src/exporting/process_data.c
+1 -1
@@ -139,7 +139,7 @@ NETDATA_DOUBLE exporting_calculate_value_from_stored_data(
139 counter += sp.count;
140 }
141 storage_engine_query_finalize(&handle);
142 - telemetry_queries_exporters_query_completed(points_read);
142 + pulse_queries_exporters_query_completed(points_read);
143
144 if (unlikely(!counter)) {
145 netdata_log_debug(
src/exporting/send_internal_metrics.c
+3 -3
@@ -11,7 +11,7 @@
11 */
12 void create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_system)
13 {
14 - if (!telemetry_enabled)
14 + if (!pulse_enabled)
15 return;
16
17 if (*st_rusage && *rd_user && *rd_system)
@@ -44,7 +44,7 @@ void create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_
44 */
45 void send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system)
46 {
47 - if (!telemetry_enabled)
47 + if (!pulse_enabled)
48 return;
49
50 struct rusage thread;
@@ -65,7 +65,7 @@ void send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system)
65 */
66 void send_internal_metrics(struct instance *instance)
67 {
68 - if (!telemetry_enabled)
68 + if (!pulse_enabled)
69 return;
70
71 struct stats *stats = &instance->stats;
src/health/health.c
+1 -1
@@ -5,7 +5,7 @@
5
6 struct health_plugin_globals health_globals = {
7 .initialization = {
8 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
8 + .spinlock = SPINLOCK_INITIALIZER,
9 .done = false,
10 },
11 .config = {
src/health/rrdcalc.c
+12 -9
@@ -329,15 +329,18 @@ static void rrdcalc_rrdhost_insert_callback(const DICTIONARY_ITEM *item __maybe_
329 if(!rc->config.units)
330 rc->config.units = string_dup(st->units);
331
332 - if(rc->config.update_every < rc->rrdset->update_every) {
333 - netdata_log_info(
334 - "HEALTH: alert '%s.%s' has update every %d, less than chart update every %d. "
335 - "Setting alarm update frequency to %d.",
336 - string2str(st->id), string2str(rc->config.name),
337 - rc->config.update_every, rc->rrdset->update_every, rc->rrdset->update_every);
338 -
339 - rc->config.update_every = st->update_every;
340 - }
332 + // the following interferes with replication, changing the alert frequency to unexpected values
333 + // let's respect user configuration, so we disable it
334 +
335 +// if(rc->config.update_every < rc->rrdset->update_every) {
336 +// netdata_log_info(
337 +// "HEALTH: alert '%s.%s' has update every %d, less than chart update every %d. "
338 +// "Setting alarm update frequency to %d.",
339 +// string2str(st->id), string2str(rc->config.name),
340 +// rc->config.update_every, rc->rrdset->update_every, rc->rrdset->update_every);
341 +//
342 +// rc->config.update_every = st->update_every;
343 +// }
344
345 rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->config.name, &rc->next_event_id, &rc->config.hash_id);
346
src/libnetdata/aral/aral.c
+233 -131
@@ -11,18 +11,17 @@
11 #define TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS
12 #endif
13
14 -// max file size
14 +// max mapped file size
15 #define ARAL_MAX_PAGE_SIZE_MMAP (1ULL * 1024 * 1024 * 1024)
16
17 // max malloc size
18 // optimal at current versions of libc is up to 256k
19 // ideal to have the same overhead as libc is 4k
20 -#define ARAL_MAX_PAGE_SIZE_MALLOC (256ULL * 1024)
20 +#define ARAL_MAX_PAGE_SIZE_MALLOC (1ULL * 1024 * 1024)
21
22 -// we don't need alignof(max_align_t) for normal C structures
23 -// alignof(uintptr_r) is sufficient for our use cases
24 -// #define SYSTEM_REQUIRED_ALIGNMENT (alignof(max_align_t))
25 -#define SYSTEM_REQUIRED_ALIGNMENT (alignof(uintptr_t))
22 +// in malloc mode, when the page is bigger than this
23 +// use anonymous private mmap pages
24 +#define ARAL_MMAP_PAGES_ABOVE (32ULL * 1024)
25
26 typedef struct aral_free {
27 size_t size;
@@ -31,6 +30,8 @@ typedef struct aral_free {
30
31 typedef struct aral_page {
32 bool marked;
33 + bool started_marked;
34 + bool mapped;
35 uint32_t size; // the allocation size of the page
36 const char *filename;
37 uint8_t *data;
@@ -62,6 +63,7 @@ struct aral_ops {
63 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
67 } atomic;
68
69 struct {
@@ -80,8 +82,7 @@ struct aral {
82
83 size_t element_size; // calculated to take into account ARAL overheads
84 size_t max_allocation_size; // calculated in bytes
83 - size_t max_page_elements; // calculated
84 - size_t page_ptr_offset; // calculated
85 + size_t element_ptr_offset; // calculated
86 size_t system_page_size; // calculated
87
88 size_t initial_page_elements;
@@ -161,30 +162,19 @@ struct aral_statistics *aral_get_statistics(ARAL *ar) {
162 return ar->stats;
163 }
164
164 -static inline size_t memory_alignment(size_t size, size_t alignment) {
165 - // return (size + alignment - 1) & ~(alignment - 1); // assumees alignment is power of 2
166 - return ((size + alignment - 1) / alignment) * alignment;
167 -}
168 -
169 -static size_t aral_align_alloc_size(ARAL *ar, uint64_t size) {
170 - size = memory_alignment(size, ar->config.system_page_size);
171 -
172 - if(size % ar->config.element_size)
173 - size -= size % ar->config.element_size;
174 -
175 - return size;
176 -}
177 -
178 -static inline void aral_lock(ARAL *ar) {
165 +static inline void aral_lock_with_trace(ARAL *ar, const char *func) {
166 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
180 - spinlock_lock(&ar->aral_lock.spinlock);
167 + spinlock_lock_with_trace(&ar->aral_lock.spinlock, func);
168 }
169
183 -static inline void aral_unlock(ARAL *ar) {
170 +static inline void aral_unlock_with_trace(ARAL *ar, const char *func) {
171 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
185 - spinlock_unlock(&ar->aral_lock.spinlock);
172 + spinlock_unlock_with_trace(&ar->aral_lock.spinlock, func);
173 }
174
175 +#define aral_lock(ar) aral_lock_with_trace(ar, __FUNCTION__)
176 +#define aral_unlock(ar) aral_unlock_with_trace(ar, __FUNCTION__)
177 +
178 static inline void aral_page_free_lock(ARAL *ar, ARAL_PAGE *page) {
179 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
180 spinlock_lock(&page->free.spinlock);
@@ -318,13 +308,13 @@ static inline ARAL_PAGE *find_page_with_allocation_internal_check(ARAL *ar, void
308 }
309 #endif
310
321 -// ----------------------------------------------------------------------------
311 +// --------------------------------------------------------------------------------------------------------------------
312 // Tagging the pointer with the 'marked' flag
313
314 // Retrieving the pointer and the 'marked' flag
315 static ARAL_PAGE *aral_get_page_pointer_after_element___do_NOT_have_aral_lock(ARAL *ar, void *ptr, bool *marked) {
316 uint8_t *data = ptr;
327 - uintptr_t *page_ptr = (uintptr_t *)&data[ar->config.page_ptr_offset];
317 + uintptr_t *page_ptr = (uintptr_t *)&data[ar->config.element_ptr_offset];
318 uintptr_t tagged_page = __atomic_load_n(page_ptr, __ATOMIC_ACQUIRE); // Atomically load the tagged pointer
319 *marked = (tagged_page & 1) != 0; // Extract the LSB as the 'marked' flag
320 ARAL_PAGE *page = (ARAL_PAGE *)(tagged_page & ~1); // Mask out the LSB to get the original pointer
@@ -358,13 +348,13 @@ static ARAL_PAGE *aral_get_page_pointer_after_element___do_NOT_have_aral_lock(AR
348
349 static void aral_set_page_pointer_after_element___do_NOT_have_aral_lock(ARAL *ar, void *page, void *ptr, bool marked) {
350 uint8_t *data = ptr;
361 - uintptr_t *page_ptr = (uintptr_t *)&data[ar->config.page_ptr_offset];
351 + uintptr_t *page_ptr = (uintptr_t *)&data[ar->config.element_ptr_offset];
352 uintptr_t tagged_page = (uintptr_t)page; // Cast the pointer to an integer
353 if (marked) tagged_page |= 1; // Set the LSB to 1 if 'marked' is true
354 __atomic_store_n(page_ptr, tagged_page, __ATOMIC_RELEASE); // Atomically store the tagged pointer
355 }
356
367 -// ----------------------------------------------------------------------------
357 +// --------------------------------------------------------------------------------------------------------------------
358 // check a free slot
359
360 #ifdef NETDATA_INTERNAL_CHECKS
@@ -381,57 +371,168 @@ static inline void aral_free_validate_internal_check(ARAL *ar, ARAL_FREE *fr) {
371 #define aral_free_validate_internal_check(ar, fr) debug_dummy()
372 #endif
373
384 -// ----------------------------------------------------------------------------
374 +// --------------------------------------------------------------------------------------------------------------------
375 +// page size management
376 +
377 +static inline size_t memory_alignment(size_t size, size_t alignment) {
378 + // return (size + alignment - 1) & ~(alignment - 1); // assumees alignment is power of 2
379 + return ((size + alignment - 1) / alignment) * alignment;
380 +}
381 +
382 +static size_t aral_get_system_page_size(void) {
383 + long int page_size = sysconf(_SC_PAGE_SIZE);
384 + if (unlikely(page_size <= 4096))
385 + return 4096;
386 + else
387 + return page_size;
388 +}
389 +
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 +
395 +static size_t aral_element_slot_size(size_t requested_element_size, bool usable) {
396 + // we need to add a page pointer after the element
397 + // so, first align the element size to the pointer size
398 + size_t element_size = memory_alignment(requested_element_size, sizeof(uintptr_t));
399 +
400 + // then add the size of a pointer to it
401 + element_size += sizeof(uintptr_t);
402 +
403 + // make sure it is at least what we need for an ARAL_FREE slot
404 + if (element_size < sizeof(ARAL_FREE))
405 + element_size = sizeof(ARAL_FREE);
406 +
407 + // and finally align it to the natural alignment
408 + element_size = memory_alignment(element_size, SYSTEM_REQUIRED_ALIGNMENT);
409 +
410 + if(usable)
411 + return element_size - sizeof(uintptr_t);
412 +
413 + return element_size;
414 +}
415 +
416 +size_t aral_optimal_malloc_page_size(void) {
417 + return ARAL_MAX_PAGE_SIZE_MALLOC;
418 +}
419
386 -size_t aral_next_allocation_size___adders_lock_needed(ARAL *ar, bool marked) {
420 +static size_t aral_elements_in_page_size(ARAL *ar, size_t page_size) {
421 + if(ar->config.mmap.enabled)
422 + return page_size / ar->config.element_size;
423 +
424 + size_t aral_page_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT);
425 + size_t remaining = page_size - aral_page_size;
426 + return remaining / ar->config.element_size;
427 +}
428 +
429 +static size_t aral_next_allocation_size___adders_lock_needed(ARAL *ar, bool marked) {
430 size_t idx = mark_to_idx(marked);
431 size_t size = ar->ops[idx].adders.allocation_size;
432
390 - if(size > ar->config.max_allocation_size)
391 - size = ar->config.max_allocation_size;
392 - else
393 - ar->ops[idx].adders.allocation_size = aral_align_alloc_size(ar, (uint64_t)ar->ops[idx].adders.allocation_size * 2);
433 + bool last_allocated = __atomic_load_n(&ar->ops[idx].atomic.last_allocated_or_deallocated, __ATOMIC_RELAXED);
434 + if(last_allocated) {
435 + size *= 2;
436 + if(size > ar->config.max_allocation_size)
437 + size = ar->config.max_allocation_size;
438 + ar->ops[idx].adders.allocation_size = size;
439 + }
440 +
441 + if(!ar->config.mmap.enabled && size < ARAL_MMAP_PAGES_ABOVE) {
442 + // when doing malloc, don't allocate entire pages, but only what needed
443 + size =
444 + aral_elements_in_page_size(ar, size) * ar->config.element_size +
445 + memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT);
446 + }
447 +
448 + __atomic_store_n(&ar->ops[idx].atomic.last_allocated_or_deallocated, true, __ATOMIC_RELAXED);
449
450 return size;
451 }
452
398 -static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
399 - ARAL_PAGE *page = callocz(1, sizeof(ARAL_PAGE));
400 - spinlock_init(&page->free.spinlock);
401 - page->size = size;
402 - page->max_elements = page->size / ar->config.element_size;
403 - page->aral_lock.free_elements = page->max_elements;
404 -
405 - __atomic_add_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
406 - __atomic_add_fetch(&ar->stats->structures.allocated_bytes, sizeof(ARAL_PAGE), __ATOMIC_RELAXED);
453 +// --------------------------------------------------------------------------------------------------------------------
454
408 - if(unlikely(ar->config.mmap.enabled)) {
455 +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;
457 + ARAL_PAGE *page;
458 + if(ar->config.mmap.enabled) {
459 + page = callocz(1, sizeof(ARAL_PAGE));
460 ar->aral_lock.file_number++;
461 +
462 char filename[FILENAME_MAX + 1];
463 snprintfz(filename, FILENAME_MAX, "%s/array_alloc.mmap/%s.%zu", *ar->config.mmap.cache_dir, ar->config.mmap.filename, ar->aral_lock.file_number);
464 page->filename = strdupz(filename);
413 - page->data = netdata_mmap(page->filename, page->size, MAP_SHARED, 0, false, NULL);
465 + page->mapped = true;
466 +
467 + page->data = netdata_mmap(page->filename, size, MAP_SHARED, 0, false, NULL);
468 if (unlikely(!page->data))
415 - fatal("ARAL: '%s' cannot allocate aral buffer of size %u on filename '%s'",
416 - ar->config.name, page->size, page->filename);
469 + fatal("ARAL: '%s' cannot allocate aral buffer of size %zu on filename '%s'",
470 + ar->config.name, size, page->filename);
471 +
472 __atomic_add_fetch(&ar->stats->mmap.allocations, 1, __ATOMIC_RELAXED);
418 - __atomic_add_fetch(&ar->stats->mmap.allocated_bytes, page->size, __ATOMIC_RELAXED);
473 + __atomic_add_fetch(&ar->stats->mmap.allocated_bytes, size, __ATOMIC_RELAXED);
474 + data_size = size;
475 + structures_size = sizeof(ARAL_PAGE);
476 }
420 - else {
477 #ifdef NETDATA_TRACE_ALLOCATIONS
422 - page->data = mallocz_int(page->size TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
478 + else {
479 + page = callocz(1, sizeof(ARAL_PAGE));
480 + page->data = mallocz_int(size TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
481 + page->mapped = false;
482 + __atomic_add_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
483 + __atomic_add_fetch(&ar->stats->malloc.allocated_bytes, size, __ATOMIC_RELAXED);
484 + }
485 #else
424 - page->data = mallocz(page->size);
486 + else {
487 + 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;
491 +
492 + if (size >= ARAL_MMAP_PAGES_ABOVE) {
493 + bool mapped;
494 + uint8_t *ptr = netdata_mmap(NULL, size, MAP_PRIVATE, 1, false, NULL);
495 + if (ptr) {
496 + 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);
499 + }
500 + else {
501 + ptr = mallocz(size);
502 + 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);
505 + }
506 + page = (ARAL_PAGE *)ptr;
507 + memset(page, 0, ARAL_PAGE_size);
508 + page->data = &ptr[ARAL_PAGE_size];
509 + page->mapped = mapped;
510 + }
511 + else {
512 + uint8_t *ptr = mallocz(size);
513 + page = (ARAL_PAGE *)ptr;
514 + memset(page, 0, ARAL_PAGE_size);
515 + page->data = &ptr[ARAL_PAGE_size];
516 + page->mapped = false;
517 +
518 + __atomic_add_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
519 + __atomic_add_fetch(&ar->stats->malloc.allocated_bytes, data_size, __ATOMIC_RELAXED);
520 + }
521 + }
522 #endif
523
427 - __atomic_add_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
428 - __atomic_add_fetch(&ar->stats->malloc.allocated_bytes, page->size, __ATOMIC_RELAXED);
429 - }
524 + spinlock_init(&page->free.spinlock);
525 + page->size = size;
526 + page->max_elements = aral_elements_in_page_size(ar, page->size);
527 + page->aral_lock.free_elements = page->max_elements;
528 +
529 + __atomic_add_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
530 + __atomic_add_fetch(&ar->stats->structures.allocated_bytes, structures_size, __ATOMIC_RELAXED);
531
532 // link the free space to its page
533 ARAL_FREE *fr = (ARAL_FREE *)page->data;
534
434 - fr->size = page->size;
535 + fr->size = data_size;
536 fr->next = NULL;
537 page->free.list = fr;
538
@@ -441,33 +542,55 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
542 }
543
544 void aral_del_page___no_lock_needed(ARAL *ar, ARAL_PAGE *page TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
545 + size_t idx = mark_to_idx(page->started_marked);
546 + __atomic_store_n(&ar->ops[idx].atomic.last_allocated_or_deallocated, true, __ATOMIC_RELAXED);
547 +
548 + size_t data_size, structures_size;
549
550 // free it
551 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);
557 +
558 netdata_munmap(page->data, page->size);
559
560 if (unlikely(unlink(page->filename) == 1))
561 netdata_log_error("Cannot delete file '%s'", page->filename);
562
563 freez((void *)page->filename);
453 -
454 - __atomic_sub_fetch(&ar->stats->mmap.allocations, 1, __ATOMIC_RELAXED);
455 - __atomic_sub_fetch(&ar->stats->mmap.allocated_bytes, page->size, __ATOMIC_RELAXED);
564 + freez(page);
565 }
566 else {
567 #ifdef NETDATA_TRACE_ALLOCATIONS
568 + __atomic_sub_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
569 + __atomic_sub_fetch(&ar->stats->malloc.allocated_bytes, page->size - sizeof(ARAL_PAGE), __ATOMIC_RELAXED);
570 +
571 freez_int(page->data TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
572 + freez(page);
573 #else
461 - freez(page->data);
574 + data_size = page->max_elements * ar->config.element_size;
575 + structures_size = page->size - data_size;
576 +
577 + 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 +
581 + netdata_munmap(page, page->size);
582 + }
583 + 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 +
587 + freez(page);
588 + }
589 #endif
463 - __atomic_sub_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
464 - __atomic_sub_fetch(&ar->stats->malloc.allocated_bytes, page->size, __ATOMIC_RELAXED);
590 }
591
467 - freez(page);
468 -
592 __atomic_sub_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
470 - __atomic_sub_fetch(&ar->stats->structures.allocated_bytes, sizeof(ARAL_PAGE), __ATOMIC_RELAXED);
593 + __atomic_sub_fetch(&ar->stats->structures.allocated_bytes, structures_size, __ATOMIC_RELAXED);
594 }
595
596 static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
@@ -502,7 +625,7 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
625 if(ar->ops[idx].adders.allocating_elements + threads_currently_deallocating < threads_currently_allocating) {
626 can_add = true;
627 page_allocation_size = aral_next_allocation_size___adders_lock_needed(ar, marked);
505 - ar->ops[idx].adders.allocating_elements += page_allocation_size / ar->config.element_size;
628 + ar->ops[idx].adders.allocating_elements += aral_elements_in_page_size(ar, page_allocation_size);
629 }
630 aral_adders_unlock(ar, marked);
631 }
@@ -510,7 +633,7 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
633
634 if(can_add) {
635 page = aral_create_page___no_lock_needed(ar, page_allocation_size TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
513 - page->marked = marked;
636 + page->marked = page->started_marked = marked;
637
638 aral_lock(ar);
639
@@ -521,7 +644,7 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
644 //#endif
645
646 aral_adders_lock(ar, marked);
524 - ar->ops[idx].adders.allocating_elements -= page_allocation_size / ar->config.element_size;
647 + ar->ops[idx].adders.allocating_elements -= aral_elements_in_page_size(ar, page_allocation_size);
648 aral_adders_unlock(ar, marked);
649
650 // we have a page that is all empty
@@ -836,38 +959,6 @@ size_t aral_actual_element_size(ARAL *ar) {
959 return ar->config.element_size;
960 }
961
839 -static size_t aral_allocation_slot_size(size_t requested_element_size, bool usable) {
840 - // we need to add a page pointer after the element
841 - // so, first align the element size to the pointer size
842 - size_t element_size = memory_alignment(requested_element_size, sizeof(uintptr_t));
843 -
844 - // then add the size of a pointer to it
845 - element_size += sizeof(uintptr_t);
846 -
847 - // make sure it is at least what we need for an ARAL_FREE slot
848 - if (element_size < sizeof(ARAL_FREE))
849 - element_size = sizeof(ARAL_FREE);
850 -
851 - // and finally align it to the natural alignment
852 - element_size = memory_alignment(element_size, SYSTEM_REQUIRED_ALIGNMENT);
853 -
854 - if(usable)
855 - return element_size - sizeof(uintptr_t);
856 -
857 - return element_size;
858 -}
859 -
860 -size_t aral_optimal_page_size(void) {
861 - return ARAL_MAX_PAGE_SIZE_MALLOC;
862 -}
863 -
864 -static void optimal_max_page_size(ARAL *ar) {
865 - if(ar->config.requested_max_page_size)
866 - return;
867 -
868 - ar->config.requested_max_page_size = aral_optimal_page_size();
869 -}
870 -
962 ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_elements, size_t max_page_size,
963 struct aral_statistics *stats, const char *filename, const char **cache_dir, bool mmap, bool lockless) {
964 ARAL *ar = callocz(1, sizeof(ARAL));
@@ -892,19 +983,22 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
983 ar->config.options |= ARAL_ALLOCATED_STATS;
984 }
985
895 - long int page_size = sysconf(_SC_PAGE_SIZE);
896 - if (unlikely(page_size == -1))
897 - ar->config.system_page_size = 4096;
898 - else
899 - ar->config.system_page_size = page_size;
986 + // ----------------------------------------------------------------------------------------------------------------
987 + // disable mmap if the directories are not given
988 +
989 + if(ar->config.mmap.enabled && (!ar->config.mmap.cache_dir || !*ar->config.mmap.cache_dir)) {
990 + netdata_log_error("ARAL: '%s' mmap cache directory is not configured properly, disabling mmap.", ar->config.name);
991 + ar->config.mmap.enabled = false;
992 + internal_fatal(true, "ARAL: '%s' mmap cache directory is not configured properly", ar->config.name);
993 + }
994
901 - ar->config.element_size = aral_allocation_slot_size(ar->config.requested_element_size, false);
902 - optimal_max_page_size(ar);
995 + // ----------------------------------------------------------------------------------------------------------------
996 + // calculate element size, after adding our pointer
997
904 - ar->config.max_page_elements = ar->config.requested_max_page_size / ar->config.element_size;
998 + ar->config.element_size = aral_element_slot_size(ar->config.requested_element_size, false);
999
1000 // we write the page pointer just after each element
907 - ar->config.page_ptr_offset = ar->config.element_size - sizeof(uintptr_t);
1001 + ar->config.element_ptr_offset = ar->config.element_size - sizeof(uintptr_t);
1002
1003 if(ar->config.requested_element_size + sizeof(uintptr_t) > ar->config.element_size)
1004 fatal("ARAL: '%s' failed to calculate properly page_ptr_offset: "
@@ -912,35 +1006,42 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
1006 "final element size %zu, page_ptr_offset %zu",
1007 ar->config.name, ar->config.requested_element_size, sizeof(uintptr_t),
1008 SYSTEM_REQUIRED_ALIGNMENT,
915 - ar->config.element_size, ar->config.page_ptr_offset);
1009 + ar->config.element_size, ar->config.element_ptr_offset);
1010
917 - //netdata_log_info("ARAL: element size %zu, sizeof(uintptr_t) %zu, natural alignment %zu, final element size %zu, page_ptr_offset %zu",
918 - // ar->element_size, sizeof(uintptr_t), ARAL_NATURAL_ALIGNMENT, ar->internal.element_size, ar->internal.page_ptr_offset);
1011 + // ----------------------------------------------------------------------------------------------------------------
1012 + // calculate allocation sizes
1013
1014 + ar->config.system_page_size = aral_get_system_page_size();
1015
1016 if (ar->config.initial_page_elements < 2)
1017 ar->config.initial_page_elements = 2;
1018
924 - if(ar->config.mmap.enabled && (!ar->config.mmap.cache_dir || !*ar->config.mmap.cache_dir)) {
925 - netdata_log_error("ARAL: '%s' mmap cache directory is not configured properly, disabling mmap.", ar->config.name);
926 - ar->config.mmap.enabled = false;
927 - internal_fatal(true, "ARAL: '%s' mmap cache directory is not configured properly", ar->config.name);
928 - }
1019 + if(!ar->config.requested_max_page_size)
1020 + ar->config.requested_max_page_size = ar->config.mmap.enabled ? ARAL_MAX_PAGE_SIZE_MMAP : ARAL_MAX_PAGE_SIZE_MALLOC;
1021
930 - uint64_t max_alloc_size;
931 - if(!ar->config.max_page_elements)
932 - max_alloc_size = ar->config.mmap.enabled ? ARAL_MAX_PAGE_SIZE_MMAP : ARAL_MAX_PAGE_SIZE_MALLOC;
933 - else
934 - max_alloc_size = ar->config.max_page_elements * ar->config.element_size;
1022 + // calculate the maximum allocation size we will do
1023 + ar->config.max_allocation_size =
1024 + memory_alignment(ar->config.requested_max_page_size, ar->config.system_page_size);
1025 +
1026 + // find the minimum page size we will use
1027 + size_t min_required_page_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT) + 2 * ar->config.element_size;
1028 + min_required_page_size = memory_alignment(min_required_page_size, ar->config.system_page_size);
1029 +
1030 + // make sure the maximum is enough
1031 + if(ar->config.max_allocation_size < min_required_page_size)
1032 + ar->config.max_allocation_size = min_required_page_size;
1033 +
1034 + // set the starting allocation size for both marked and unmarked partitions
1035 + ar->ops[0].adders.allocation_size = ar->ops[1].adders.allocation_size = min_required_page_size;
1036 +
1037 + // ----------------------------------------------------------------------------------------------------------------
1038
936 - ar->config.max_allocation_size = aral_align_alloc_size(ar, max_alloc_size);
937 - ar->ops[0].adders.allocation_size =
938 - ar->ops[1].adders.allocation_size =
939 - aral_align_alloc_size(ar, (uint64_t)ar->config.element_size * ar->config.initial_page_elements);
1039 ar->aral_lock.pages_free = NULL;
1040 ar->aral_lock.pages_marked_free = NULL;
1041 ar->aral_lock.file_number = 0;
1042
1043 + // ----------------------------------------------------------------------------------------------------------------
1044 +
1045 if(ar->config.mmap.enabled) {
1046 char directory_name[FILENAME_MAX + 1];
1047 snprintfz(directory_name, FILENAME_MAX, "%s/array_alloc.mmap", *ar->config.mmap.cache_dir);
@@ -972,7 +1073,7 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
1073 return ar;
1074 }
1075
975 -// ----------------------------------------------------------------------------
1076 +// --------------------------------------------------------------------------------------------------------------------
1077 // global aral caching
1078
1079 #define ARAL_BY_SIZE_MAX_SIZE 1024
@@ -1063,7 +1164,7 @@ void aral_by_size_release(ARAL *ar) {
1164 aral_destroy(ar);
1165 }
1166
1066 -// ----------------------------------------------------------------------------
1167 +// --------------------------------------------------------------------------------------------------------------------
1168 // unittest
1169
1170 struct aral_unittest_config {
@@ -1159,10 +1260,11 @@ static void *aral_test_thread(void *ptr) {
1260 pointers[i] = unittest_aral_malloc(ar, marked);
1261 }
1262
1162 - size_t increment = elements / ar->config.max_page_elements;
1263 + size_t max_page_elements = aral_elements_in_page_size(ar, ar->config.max_allocation_size);
1264 + size_t increment = elements / max_page_elements;
1265 for (size_t all = increment; all <= elements / 2; all += increment) {
1266
1165 - size_t to_free = (all % ar->config.max_page_elements) + 1;
1267 + size_t to_free = (all % max_page_elements) + 1;
1268 size_t step = elements / to_free;
1269 if(!step) step = 1;
1270
src/libnetdata/aral/aral.h
+1 -1
@@ -52,7 +52,7 @@ struct aral_statistics *aral_by_size_statistics(void);
52 size_t aral_by_size_used_bytes(void);
53 size_t aral_used_bytes_from_stats(struct aral_statistics *stats);
54
55 -size_t aral_optimal_page_size(void);
55 +size_t aral_optimal_malloc_page_size(void);
56
57 int aral_unittest(size_t elements);
58
src/libnetdata/avl/avl.h
+1 -1
@@ -13,7 +13,7 @@
13 #if defined(AVL_LOCK_WITH_RWLOCK)
14 #define AVL_LOCK_INITIALIZER NETDATA_RWLOCK_INITIALIZER
15 #else
16 -#define AVL_LOCK_INITIALIZER NETDATA_RW_SPINLOCK_INITIALIZER
16 +#define AVL_LOCK_INITIALIZER RW_SPINLOCK_INITIALIZER
17 #endif
18
19 /* Data structures */
src/libnetdata/clocks/clocks.c
+1 -1
@@ -264,7 +264,7 @@ void sleep_to_absolute_time(usec_t usec) {
264 #define HEARTBEAT_RANDOM_OFFSET_UT (350 * USEC_PER_MS)
265
266 #define HEARTBEAT_ALIGNMENT_STATISTICS_SIZE 20
267 -static SPINLOCK heartbeat_alignment_spinlock = NETDATA_SPINLOCK_INITIALIZER;
267 +static SPINLOCK heartbeat_alignment_spinlock = SPINLOCK_INITIALIZER;
268 static size_t heartbeat_alignment_id = 0;
269
270 struct heartbeat_thread_statistics {
src/libnetdata/config/appconfig.h
+2 -2
@@ -100,7 +100,7 @@
100 #define CONFIG_SECTION_PROMETHEUS "prometheus:exporter"
101 #define CONFIG_SECTION_HOST_LABEL "host labels"
102 #define EXPORTING_CONF "exporting.conf"
103 -#define CONFIG_SECTION_TELEMETRY "telemetry"
103 +#define CONFIG_SECTION_PULSE "pulse"
104 #define CONFIG_SECTION_DB "db"
105
106 // these are used to limit the configuration names and values lengths
@@ -122,7 +122,7 @@ struct config {
122
123 #define APPCONFIG_INITIALIZER (struct config) { \
124 .sections = NULL, \
125 - .spinlock = NETDATA_SPINLOCK_INITIALIZER, \
125 + .spinlock = SPINLOCK_INITIALIZER, \
126 .index = { \
127 .avl_tree = { \
128 .root = NULL, \
src/libnetdata/config/appconfig_conf_file.c
+1 -1
@@ -227,7 +227,7 @@ void appconfig_generate(struct config *root, BUFFER *wb, int only_changed, bool
227 else if(!string_strcmp(sect->name, CONFIG_SECTION_WEBRTC)) pri = 11;
228 // by default, new sections will get pri = 12 (set at the end, below)
229 else if(!string_strcmp(sect->name, CONFIG_SECTION_REGISTRY)) pri = 13;
230 - else if(!string_strcmp(sect->name, CONFIG_SECTION_TELEMETRY)) pri = 14;
230 + else if(!string_strcmp(sect->name, CONFIG_SECTION_PULSE)) pri = 14;
231 else if(!string_strcmp(sect->name, CONFIG_SECTION_PLUGINS)) pri = 15;
232 else if(!string_strcmp(sect->name, CONFIG_SECTION_STATSD)) pri = 16;
233 else if(!string_strncmp(sect->name, "plugin:", 7)) pri = 17; // << change the loop too if you change this
src/libnetdata/functions_evloop/functions_evloop.c
+9 -2
@@ -312,17 +312,24 @@ static void *rrd_functions_worker_globals_reader_main(void *arg) {
312 else
313 nd_log(NDLS_COLLECTORS, NDLP_NOTICE, "Received PROGRESS for transaction '%s', but it not available here", transaction);
314 }
315 + else if(keyword && strcmp(keyword, PLUGINSD_CALL_QUIT) == 0) {
316 + *wg->plugin_should_exit = true;
317 + break;
318 + }
319 else
320 nd_log(NDLS_COLLECTORS, NDLP_NOTICE, "Received unknown command: %s", keyword ? keyword : "(unset)");
321
322 buffer_flush(buffer);
323 }
324
321 - if(!(*wg->plugin_should_exit))
325 + int status = 0;
326 + if(!(*wg->plugin_should_exit)) {
327 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Read error on stdin");
328 + status = 1;
329 + }
330
331 *wg->plugin_should_exit = true;
325 - exit(1);
332 + exit(status);
333 }
334
335 void worker_queue_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
src/libnetdata/functions_evloop/functions_evloop.h
+2
@@ -63,6 +63,8 @@
63 #define PLUGINSD_CALL_FUNCTION_CANCEL "FUNCTION_CANCEL" // cancel a running function transaction
64 #define PLUGINSD_CALL_FUNCTION_PROGRESS "FUNCTION_PROGRESS" // let the function know the user is waiting
65
66 +#define PLUGINSD_CALL_QUIT "QUIT" // ask the plugin to quit
67 +
68 // dyncfg
69 // enabled with STREAM_CAP_DYNCFG
70 #define PLUGINSD_KEYWORD_CONFIG "CONFIG"
src/libnetdata/july/july.c
+1 -1
@@ -44,7 +44,7 @@ static struct {
44 } atomics;
45 } julyl_globals = {
46 .protected = {
47 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
47 + .spinlock = SPINLOCK_INITIALIZER,
48 .available_items = NULL,
49 .available = 0,
50 },
src/libnetdata/libnetdata.c
+4 -4
@@ -450,11 +450,11 @@ void posix_memfree(void *ptr) {
450
451 void mallocz_release_as_much_memory_to_the_system(void) {
452 #if defined(HAVE_C_MALLOPT) || defined(HAVE_C_MALLOC_TRIM)
453 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
453 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
454 spinlock_lock(&spinlock);
455
456 #ifdef HAVE_C_MALLOPT
457 - size_t trim_threshold = aral_optimal_page_size();
457 + size_t trim_threshold = aral_optimal_malloc_page_size();
458 mallopt(M_TRIM_THRESHOLD, (int)trim_threshold);
459 #endif
460
@@ -799,7 +799,7 @@ BUFFER *run_command_and_get_output_to_buffer(const char *command, int max_line_l
799 buffer[max_line_length] = '\0';
800 buffer_strcat(wb, buffer);
801 }
802 - spawn_popen_kill(pi);
802 + spawn_popen_kill(pi, 0);
803 }
804 else {
805 buffer_free(wb);
@@ -818,7 +818,7 @@ bool run_command_and_copy_output_to_stdout(const char *command, int max_line_len
818 while (fgets(buffer, max_line_length, spawn_popen_stdout(pi)))
819 fprintf(stdout, "%s", buffer);
820
821 - spawn_popen_kill(pi);
821 + spawn_popen_kill(pi, 0);
822 }
823 else {
824 netdata_log_error("Failed to execute command '%s'.", command);
src/libnetdata/libnetdata.h
+2
@@ -123,6 +123,8 @@ extern const char *netdata_configured_host_prefix;
123
124 #include "threads/threads.h"
125 #include "locks/locks.h"
126 +#include "locks/spinlock.h"
127 +#include "locks/rw-spinlock.h"
128 #include "completion/completion.h"
129 #include "clocks/clocks.h"
130 #include "simple_pattern/simple_pattern.h"
src/libnetdata/local-sockets/local-sockets.h
+1 -1
@@ -1587,7 +1587,7 @@ static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, st
1587 spinlock_unlock(&ls->spinlock);
1588 }
1589
1590 - spawn_server_exec_kill(ls->spawn_server, si);
1590 + spawn_server_exec_kill(ls->spawn_server, si, 0);
1591
1592 if(ls->config.report && received == 0)
1593 __atomic_add_fetch(&ls->stats.namespaces_forks_unresponsive, 1, __ATOMIC_RELAXED);
src/libnetdata/locks/locks.c
-250
@@ -220,256 +220,6 @@ int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) {
220 return ret;
221 }
222
223 -// ----------------------------------------------------------------------------
224 -// spinlock implementation
225 -// https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s
226 -
227 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
228 -void spinlock_init(SPINLOCK *spinlock)
229 -{
230 - netdata_mutex_init(&spinlock->inner);
231 -}
232 -#else
233 -void spinlock_init(SPINLOCK *spinlock)
234 -{
235 - memset(spinlock, 0, sizeof(SPINLOCK));
236 -}
237 -#endif
238 -
239 -#ifndef SPINLOCK_IMPL_WITH_MUTEX
240 -static inline void spinlock_lock_internal(SPINLOCK *spinlock)
241 -{
242 - #ifdef NETDATA_INTERNAL_CHECKS
243 - size_t spins = 0;
244 - #endif
245 -
246 - for(int i = 1;
247 - __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
248 - __atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)
249 - ; i++
250 - ) {
251 -
252 - #ifdef NETDATA_INTERNAL_CHECKS
253 - spins++;
254 - #endif
255 -
256 - if(unlikely(i % 8 == 0)) {
257 - if(i == 8 * 4) {
258 - i = 0;
259 - yield_the_processor();
260 - }
261 - else
262 - tinysleep();
263 - }
264 - }
265 -
266 - // we have the lock
267 -
268 - #ifdef NETDATA_INTERNAL_CHECKS
269 - spinlock->spins += spins;
270 - spinlock->locker_pid = gettid_cached();
271 - #endif
272 -
273 - nd_thread_spinlock_locked();
274 -}
275 -#endif // SPINLOCK_IMPL_WITH_MUTEX
276 -
277 -#ifndef SPINLOCK_IMPL_WITH_MUTEX
278 -static inline void spinlock_unlock_internal(SPINLOCK *spinlock)
279 -{
280 - #ifdef NETDATA_INTERNAL_CHECKS
281 - spinlock->locker_pid = 0;
282 - #endif
283 -
284 - __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE);
285 -
286 - nd_thread_spinlock_unlocked();
287 -}
288 -#endif // SPINLOCK_IMPL_WITH_MUTEX
289 -
290 -#ifndef SPINLOCK_IMPL_WITH_MUTEX
291 -static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) {
292 - if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
293 - !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) {
294 - // we got the lock
295 - nd_thread_spinlock_locked();
296 - return true;
297 - }
298 -
299 - return false;
300 -}
301 -#endif // SPINLOCK_IMPL_WITH_MUTEX
302 -
303 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
304 -void spinlock_lock(SPINLOCK *spinlock)
305 -{
306 - netdata_mutex_lock(&spinlock->inner);
307 -}
308 -#else
309 -void spinlock_lock(SPINLOCK *spinlock)
310 -{
311 - spinlock_lock_internal(spinlock);
312 -}
313 -#endif
314 -
315 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
316 -void spinlock_unlock(SPINLOCK *spinlock)
317 -{
318 - netdata_mutex_unlock(&spinlock->inner);
319 -}
320 -#else
321 -void spinlock_unlock(SPINLOCK *spinlock)
322 -{
323 - spinlock_unlock_internal(spinlock);
324 -}
325 -#endif
326 -
327 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
328 -bool spinlock_trylock(SPINLOCK *spinlock)
329 -{
330 - return netdata_mutex_trylock(&spinlock->inner) == 0;
331 -}
332 -#else
333 -bool spinlock_trylock(SPINLOCK *spinlock)
334 -{
335 - return spinlock_trylock_internal(spinlock);
336 -}
337 -#endif
338 -
339 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
340 -void spinlock_lock_cancelable(SPINLOCK *spinlock)
341 -{
342 - netdata_mutex_lock(&spinlock->inner);
343 -}
344 -#else
345 -void spinlock_lock_cancelable(SPINLOCK *spinlock)
346 -{
347 - spinlock_lock_internal(spinlock);
348 -}
349 -#endif
350 -
351 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
352 -void spinlock_unlock_cancelable(SPINLOCK *spinlock)
353 -{
354 - netdata_mutex_unlock(&spinlock->inner);
355 -}
356 -#else
357 -void spinlock_unlock_cancelable(SPINLOCK *spinlock)
358 -{
359 - spinlock_unlock_internal(spinlock);
360 -}
361 -#endif
362 -
363 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
364 -bool spinlock_trylock_cancelable(SPINLOCK *spinlock)
365 -{
366 - return netdata_mutex_trylock(&spinlock->inner) == 0;
367 -}
368 -#else
369 -bool spinlock_trylock_cancelable(SPINLOCK *spinlock)
370 -{
371 - return spinlock_trylock_internal(spinlock);
372 -}
373 -#endif
374 -
375 -// ----------------------------------------------------------------------------
376 -// rw_spinlock implementation
377 -
378 -void rw_spinlock_init(RW_SPINLOCK *rw_spinlock) {
379 - rw_spinlock->readers = 0;
380 - rw_spinlock->writers_waiting = 0;
381 - spinlock_init(&rw_spinlock->spinlock);
382 -}
383 -
384 -void rw_spinlock_read_lock(RW_SPINLOCK *rw_spinlock) {
385 - while(1) {
386 - spinlock_lock(&rw_spinlock->spinlock);
387 - if (!rw_spinlock->writers_waiting) {
388 - __atomic_add_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
389 - spinlock_unlock(&rw_spinlock->spinlock);
390 - break;
391 - }
392 -
393 - spinlock_unlock(&rw_spinlock->spinlock);
394 - yield_the_processor(); // let the writer run
395 - }
396 -
397 - nd_thread_rwspinlock_read_locked();
398 -}
399 -
400 -void rw_spinlock_read_unlock(RW_SPINLOCK *rw_spinlock) {
401 -#ifndef NETDATA_INTERNAL_CHECKS
402 - __atomic_sub_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
403 -#else
404 - int32_t x = __atomic_sub_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
405 - if(x < 0)
406 - fatal("RW_SPINLOCK: readers is negative %d", x);
407 -#endif
408 -
409 - nd_thread_rwspinlock_read_unlocked();
410 -}
411 -
412 -void rw_spinlock_write_lock(RW_SPINLOCK *rw_spinlock) {
413 - size_t spins = 0;
414 - for(size_t i = 1; true ;i++) {
415 - spinlock_lock(&rw_spinlock->spinlock);
416 -
417 - if(__atomic_load_n(&rw_spinlock->readers, __ATOMIC_RELAXED) == 0) {
418 - if(spins != 0)
419 - rw_spinlock->writers_waiting--;
420 - break;
421 - }
422 -
423 - if(spins == 0)
424 - rw_spinlock->writers_waiting++;
425 -
426 - // Busy wait until all readers have released their locks.
427 - spinlock_unlock(&rw_spinlock->spinlock);
428 - if(i == 8 * 2) {
429 - i = 0;
430 - tinysleep();
431 - }
432 - spins++;
433 - }
434 -
435 - (void)spins;
436 -
437 - nd_thread_rwspinlock_write_locked();
438 -}
439 -
440 -void rw_spinlock_write_unlock(RW_SPINLOCK *rw_spinlock) {
441 - spinlock_unlock(&rw_spinlock->spinlock);
442 - nd_thread_rwspinlock_write_unlocked();
443 -}
444 -
445 -bool rw_spinlock_tryread_lock(RW_SPINLOCK *rw_spinlock) {
446 - if(spinlock_trylock(&rw_spinlock->spinlock)) {
447 - __atomic_add_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
448 - spinlock_unlock(&rw_spinlock->spinlock);
449 - nd_thread_rwspinlock_read_locked();
450 - return true;
451 - }
452 -
453 - return false;
454 -}
455 -
456 -bool rw_spinlock_trywrite_lock(RW_SPINLOCK *rw_spinlock) {
457 - if(spinlock_trylock(&rw_spinlock->spinlock)) {
458 - if (__atomic_load_n(&rw_spinlock->readers, __ATOMIC_RELAXED) == 0) {
459 - // No readers, we've successfully acquired the write lock
460 - nd_thread_rwspinlock_write_locked();
461 - return true;
462 - }
463 - else {
464 - // There are readers, unlock the spinlock and return false
465 - spinlock_unlock(&rw_spinlock->spinlock);
466 - }
467 - }
468 -
469 - return false;
470 -}
471 -
472 -
223 #ifdef NETDATA_TRACE_RWLOCKS
224
225 // ----------------------------------------------------------------------------
src/libnetdata/locks/locks.h
-48
@@ -13,54 +13,6 @@
13 typedef pthread_mutex_t netdata_mutex_t;
14 #define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
15
16 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
17 - typedef struct netdata_spinlock
18 - {
19 - netdata_mutex_t inner;
20 - } SPINLOCK;
21 -#else
22 - typedef struct netdata_spinlock
23 - {
24 - bool locked;
25 - #ifdef NETDATA_INTERNAL_CHECKS
26 - pid_t locker_pid;
27 - size_t spins;
28 - #endif
29 - } SPINLOCK;
30 -#endif
31 -
32 -#ifdef SPINLOCK_IMPL_WITH_MUTEX
33 -#define NETDATA_SPINLOCK_INITIALIZER { .inner = PTHREAD_MUTEX_INITIALIZER }
34 -#else
35 -#define NETDATA_SPINLOCK_INITIALIZER { .locked = false }
36 -#endif
37 -
38 -void spinlock_init(SPINLOCK *spinlock);
39 -void spinlock_lock(SPINLOCK *spinlock);
40 -void spinlock_unlock(SPINLOCK *spinlock);
41 -bool spinlock_trylock(SPINLOCK *spinlock);
42 -
43 -void spinlock_lock_cancelable(SPINLOCK *spinlock);
44 -void spinlock_unlock_cancelable(SPINLOCK *spinlock);
45 -bool spinlock_trylock_cancelable(SPINLOCK *spinlock);
46 -
47 -typedef struct netdata_rw_spinlock {
48 - int32_t readers;
49 - int32_t writers_waiting;
50 - SPINLOCK spinlock;
51 -} RW_SPINLOCK;
52 -
53 -#define NETDATA_RW_SPINLOCK_INITIALIZER \
54 - { .readers = 0, .spinlock = NETDATA_SPINLOCK_INITIALIZER }
55 -
56 -void rw_spinlock_init(RW_SPINLOCK *rw_spinlock);
57 -void rw_spinlock_read_lock(RW_SPINLOCK *rw_spinlock);
58 -void rw_spinlock_read_unlock(RW_SPINLOCK *rw_spinlock);
59 -void rw_spinlock_write_lock(RW_SPINLOCK *rw_spinlock);
60 -void rw_spinlock_write_unlock(RW_SPINLOCK *rw_spinlock);
61 -bool rw_spinlock_tryread_lock(RW_SPINLOCK *rw_spinlock);
62 -bool rw_spinlock_trywrite_lock(RW_SPINLOCK *rw_spinlock);
63 -
16 #ifdef NETDATA_TRACE_RWLOCKS
17
18 typedef enum {
src/libnetdata/locks/rw-spinlock.c new
+103
@@ -0,0 +1,103 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "libnetdata/libnetdata.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// rw_spinlock implementation
7 +
8 +void rw_spinlock_init_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
9 + rw_spinlock->readers = 0;
10 + rw_spinlock->writers_waiting = 0;
11 + spinlock_init_with_trace(&rw_spinlock->spinlock, func);
12 +}
13 +
14 +void rw_spinlock_read_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
15 + size_t spins = 0;
16 + while(1) {
17 + spinlock_lock_with_trace(&rw_spinlock->spinlock, func);
18 + if (!rw_spinlock->writers_waiting) {
19 + __atomic_add_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
20 + spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
21 + break;
22 + }
23 +
24 + spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
25 + yield_the_processor(); // let the writer run
26 + spins++;
27 + }
28 +
29 + worker_spinlock_contention(func, spins);
30 + nd_thread_rwspinlock_read_locked();
31 +}
32 +
33 +void rw_spinlock_read_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
34 +#ifndef NETDATA_INTERNAL_CHECKS
35 + __atomic_sub_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
36 +#else
37 + int32_t x = __atomic_sub_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
38 + if(x < 0)
39 + fatal("RW_SPINLOCK: readers is negative %d", x);
40 +#endif
41 +
42 + nd_thread_rwspinlock_read_unlocked();
43 +}
44 +
45 +void rw_spinlock_write_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
46 + size_t spins = 0;
47 + for(size_t i = 1; true ;i++) {
48 + spinlock_lock_with_trace(&rw_spinlock->spinlock, func);
49 +
50 + if(__atomic_load_n(&rw_spinlock->readers, __ATOMIC_RELAXED) == 0) {
51 + if(spins != 0)
52 + rw_spinlock->writers_waiting--;
53 + break;
54 + }
55 +
56 + if(spins == 0)
57 + rw_spinlock->writers_waiting++;
58 +
59 + // Busy wait until all readers have released their locks.
60 + spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
61 + if(i == 8 * 2) {
62 + i = 0;
63 + tinysleep();
64 + }
65 + spins++;
66 + }
67 +
68 + worker_spinlock_contention(func, spins);
69 + nd_thread_rwspinlock_write_locked();
70 +}
71 +
72 +void rw_spinlock_write_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
73 + spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
74 + nd_thread_rwspinlock_write_unlocked();
75 +}
76 +
77 +bool rw_spinlock_tryread_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
78 + if(spinlock_trylock_with_trace(&rw_spinlock->spinlock, func)) {
79 + __atomic_add_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
80 + spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
81 + nd_thread_rwspinlock_read_locked();
82 + return true;
83 + }
84 +
85 + return false;
86 +}
87 +
88 +bool rw_spinlock_trywrite_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
89 + if(spinlock_trylock_with_trace(&rw_spinlock->spinlock, func)) {
90 + if (__atomic_load_n(&rw_spinlock->readers, __ATOMIC_RELAXED) == 0) {
91 + // No readers, we've successfully acquired the write lock
92 + nd_thread_rwspinlock_write_locked();
93 + return true;
94 + }
95 + else {
96 + // There are readers, unlock the spinlock and return false
97 + spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
98 + }
99 + }
100 +
101 + return false;
102 +}
103 +
src/libnetdata/locks/rw-spinlock.h new
+34
@@ -0,0 +1,34 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_RW_SPINLOCK_H
4 +#define NETDATA_RW_SPINLOCK_H
5 +
6 +#include "libnetdata/common.h"
7 +#include "spinlock.h"
8 +
9 +typedef struct netdata_rw_spinlock {
10 + int32_t readers;
11 + int32_t writers_waiting;
12 + SPINLOCK spinlock;
13 +} RW_SPINLOCK;
14 +
15 +#define RW_SPINLOCK_INITIALIZER { .readers = 0, .spinlock = SPINLOCK_INITIALIZER}
16 +
17 +void rw_spinlock_init_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
18 +void rw_spinlock_read_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
19 +void rw_spinlock_read_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
20 +void rw_spinlock_write_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
21 +void rw_spinlock_write_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
22 +bool rw_spinlock_tryread_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
23 +bool rw_spinlock_trywrite_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
24 +
25 +
26 +#define rw_spinlock_init(rw_spinlock) rw_spinlock_init_with_trace(rw_spinlock, __FUNCTION__)
27 +#define rw_spinlock_read_lock(rw_spinlock) rw_spinlock_read_lock_with_trace(rw_spinlock, __FUNCTION__)
28 +#define rw_spinlock_read_unlock(rw_spinlock) rw_spinlock_read_unlock_with_trace(rw_spinlock, __FUNCTION__)
29 +#define rw_spinlock_write_lock(rw_spinlock) rw_spinlock_write_lock_with_trace(rw_spinlock, __FUNCTION__)
30 +#define rw_spinlock_write_unlock(rw_spinlock) rw_spinlock_write_unlock_with_trace(rw_spinlock, __FUNCTION__)
31 +#define rw_spinlock_tryread_lock(rw_spinlock) rw_spinlock_tryread_lock_with_trace(rw_spinlock, __FUNCTION__)
32 +#define rw_spinlock_trywrite_lock(rw_spinlock) rw_spinlock_trywrite_lock_with_trace(rw_spinlock, __FUNCTION__)
33 +
34 +#endif //NETDATA_RW_SPINLOCK_H
src/libnetdata/locks/spinlock.c new
+63
@@ -0,0 +1,63 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "libnetdata/libnetdata.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// spinlock implementation
7 +// https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s
8 +
9 +#ifndef SPINLOCK_IMPL_WITH_MUTEX
10 +
11 +void spinlock_init_with_trace(SPINLOCK *spinlock, const char *func __maybe_unused) {
12 + memset(spinlock, 0, sizeof(SPINLOCK));
13 +}
14 +
15 +void spinlock_lock_with_trace(SPINLOCK *spinlock, const char *func) {
16 + size_t spins = 0;
17 +
18 + for(int i = 1;
19 + __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
20 + __atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)
21 + ; i++
22 + ) {
23 +
24 + spins++;
25 + if(unlikely(i % 8 == 0)) {
26 + i = 0;
27 + tinysleep();
28 + }
29 + }
30 +
31 + // we have the lock
32 +
33 +#ifdef NETDATA_INTERNAL_CHECKS
34 + spinlock->spins += spins;
35 + spinlock->locker_pid = gettid_cached();
36 +#endif
37 +
38 + nd_thread_spinlock_locked();
39 + worker_spinlock_contention(func, spins);
40 +}
41 +
42 +void spinlock_unlock_with_trace(SPINLOCK *spinlock, const char *func __maybe_unused) {
43 +#ifdef NETDATA_INTERNAL_CHECKS
44 + spinlock->locker_pid = 0;
45 +#endif
46 +
47 + __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE);
48 +
49 + nd_thread_spinlock_unlocked();
50 +}
51 +
52 +bool spinlock_trylock_with_trace(SPINLOCK *spinlock, const char *func __maybe_unused) {
53 + if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
54 + !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) {
55 + // we got the lock
56 + nd_thread_spinlock_locked();
57 + return true;
58 + }
59 +
60 + return false;
61 +}
62 +
63 +#endif // SPINLOCK_IMPL_WITH_MUTEX
src/libnetdata/locks/spinlock.h new
+48
@@ -0,0 +1,48 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_SPINLOCK_H
4 +#define NETDATA_SPINLOCK_H
5 +
6 +#include "libnetdata/common.h"
7 +
8 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
9 +typedef struct netdata_spinlock
10 +{
11 + netdata_mutex_t inner;
12 +} SPINLOCK;
13 +#else
14 +typedef struct netdata_spinlock
15 +{
16 + bool locked;
17 +#ifdef NETDATA_INTERNAL_CHECKS
18 + pid_t locker_pid;
19 + size_t spins;
20 +#endif
21 +} SPINLOCK;
22 +#endif
23 +
24 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
25 +#define SPINLOCK_INITIALIZER { .inner = PTHREAD_MUTEX_INITIALIZER }
26 +
27 +#define spinlock_lock(spinlock) netdata_mutex_lock(&((spinlock)->inner))
28 +#define spinlock_unlock(spinlock) netdata_mutex_unlock(&((spinlock)->inner))
29 +#define spinlock_trylock(spinlock) (netdata_mutex_trylock(&((spinlock)->inner)) == 0)
30 +#define spinlock_init(spinlock) netdata_mutex_init(&((spinlock)->inner)
31 +#else
32 +#define SPINLOCK_INITIALIZER { .locked = false }
33 +
34 +void spinlock_init_with_trace(SPINLOCK *spinlock, const char *func);
35 +#define spinlock_init(spinlock) spinlock_init_with_trace(spinlock, __FUNCTION__)
36 +
37 +void spinlock_lock_with_trace(SPINLOCK *spinlock, const char *func);
38 +#define spinlock_lock(spinlock) spinlock_lock_with_trace(spinlock, __FUNCTION__)
39 +
40 +void spinlock_unlock_with_trace(SPINLOCK *spinlock, const char *func __maybe_unused);
41 +#define spinlock_unlock(spinlock) spinlock_unlock_with_trace(spinlock, __FUNCTION__)
42 +
43 +bool spinlock_trylock_with_trace(SPINLOCK *spinlock, const char *func __maybe_unused);
44 +#define spinlock_trylock(spinlock) spinlock_trylock_with_trace(spinlock, __FUNCTION__)
45 +
46 +#endif
47 +
48 +#endif //NETDATA_SPINLOCK_H
src/libnetdata/log/nd_log-internals.c
+9 -9
@@ -301,16 +301,16 @@ struct nd_log nd_log = {
301 },
302 #endif
303 .std_output = {
304 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
304 + .spinlock = SPINLOCK_INITIALIZER,
305 .initialized = false,
306 },
307 .std_error = {
308 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
308 + .spinlock = SPINLOCK_INITIALIZER,
309 .initialized = false,
310 },
311 .sources = {
312 [NDLS_UNSET] = {
313 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
313 + .spinlock = SPINLOCK_INITIALIZER,
314 .method = NDLM_DISABLED,
315 .format = NDLF_JOURNAL,
316 .filename = NULL,
@@ -320,7 +320,7 @@ struct nd_log nd_log = {
320 .limits = ND_LOG_LIMITS_UNLIMITED,
321 },
322 [NDLS_ACCESS] = {
323 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
323 + .spinlock = SPINLOCK_INITIALIZER,
324 .method = NDLM_DEFAULT,
325 .format = NDLF_LOGFMT,
326 .filename = LOG_DIR "/access.log",
@@ -330,7 +330,7 @@ struct nd_log nd_log = {
330 .limits = ND_LOG_LIMITS_UNLIMITED,
331 },
332 [NDLS_ACLK] = {
333 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
333 + .spinlock = SPINLOCK_INITIALIZER,
334 .method = NDLM_FILE,
335 .format = NDLF_LOGFMT,
336 .filename = LOG_DIR "/aclk.log",
@@ -340,7 +340,7 @@ struct nd_log nd_log = {
340 .limits = ND_LOG_LIMITS_UNLIMITED,
341 },
342 [NDLS_COLLECTORS] = {
343 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
343 + .spinlock = SPINLOCK_INITIALIZER,
344 .method = NDLM_DEFAULT,
345 .format = NDLF_LOGFMT,
346 .filename = LOG_DIR "/collector.log",
@@ -350,7 +350,7 @@ struct nd_log nd_log = {
350 .limits = ND_LOG_LIMITS_DEFAULT,
351 },
352 [NDLS_DEBUG] = {
353 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
353 + .spinlock = SPINLOCK_INITIALIZER,
354 .method = NDLM_DISABLED,
355 .format = NDLF_LOGFMT,
356 .filename = LOG_DIR "/debug.log",
@@ -360,7 +360,7 @@ struct nd_log nd_log = {
360 .limits = ND_LOG_LIMITS_UNLIMITED,
361 },
362 [NDLS_DAEMON] = {
363 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
363 + .spinlock = SPINLOCK_INITIALIZER,
364 .method = NDLM_DEFAULT,
365 .filename = LOG_DIR "/daemon.log",
366 .format = NDLF_LOGFMT,
@@ -370,7 +370,7 @@ struct nd_log nd_log = {
370 .limits = ND_LOG_LIMITS_DEFAULT,
371 },
372 [NDLS_HEALTH] = {
373 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
373 + .spinlock = SPINLOCK_INITIALIZER,
374 .method = NDLM_DEFAULT,
375 .format = NDLF_LOGFMT,
376 .filename = LOG_DIR "/health.log",
src/libnetdata/log/nd_log-to-windows-events.c
+1 -1
@@ -439,7 +439,7 @@ static bool nd_logger_windows(struct nd_log_source *source, struct log_field *fi
439
440 CLEAN_BUFFER *tmp = NULL;
441
442 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
442 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
443 spinlock_lock(&spinlock);
444 wevt_generate_all_fields_unsafe(fields, fields_max, &tmp);
445
src/libnetdata/os/random.c
+1 -1
@@ -3,7 +3,7 @@
3 #include "libnetdata/libnetdata.h"
4
5 #if !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_RAND_S)
6 -static SPINLOCK random_lock = NETDATA_SPINLOCK_INITIALIZER;
6 +static SPINLOCK random_lock = SPINLOCK_INITIALIZER;
7 static __attribute__((constructor)) void random_seed() {
8 // Use current time and process ID to create a high-entropy seed
9 struct timeval tv;
src/libnetdata/os/system-maps/cache-host-users-and-groups.c
+2 -2
@@ -53,7 +53,7 @@ static size_t read_passwd_or_group(const char *filename, struct timespec *last_m
53 void update_cached_host_users(void) {
54 if(!netdata_configured_host_prefix || !*netdata_configured_host_prefix) return;
55
56 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
56 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
57 if(!spinlock_trylock(&spinlock)) return;
58
59 char filename[FILENAME_MAX];
@@ -78,7 +78,7 @@ void update_cached_host_users(void) {
78 void update_cached_host_groups(void) {
79 if(!netdata_configured_host_prefix || !*netdata_configured_host_prefix) return;
80
81 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
81 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
82 if(!spinlock_trylock(&spinlock)) return;
83
84 char filename[FILENAME_MAX];
src/libnetdata/os/system-maps/cached-gid-groupname.c
+1 -1
@@ -19,7 +19,7 @@ static struct {
19 SIMPLE_HASHTABLE_GROUPNAMES_CACHE ht;
20 } group_cache = {
21 .initialized = false,
22 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
22 + .spinlock = SPINLOCK_INITIALIZER,
23 .ht = { 0 },
24 };
25
src/libnetdata/os/system-maps/cached-sid-username.c
+1 -1
@@ -35,7 +35,7 @@ static struct {
35 SPINLOCK spinlock;
36 struct simple_hashtable_SID hashtable;
37 } sid_globals = {
38 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
38 + .spinlock = SPINLOCK_INITIALIZER,
39 .hashtable = { 0 },
40 };
41
src/libnetdata/os/system-maps/cached-uid-username.c
+1 -1
@@ -19,7 +19,7 @@ static struct {
19 SIMPLE_HASHTABLE_USERNAMES_CACHE ht;
20 } user_cache = {
21 .initialized = false,
22 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
22 + .spinlock = SPINLOCK_INITIALIZER,
23 .ht = { 0 },
24 };
25
src/libnetdata/os/windows-perflib/perflib-names.c
+1 -1
@@ -33,7 +33,7 @@ static struct {
33 struct simple_hashtable_PERFLIB hashtable;
34 FILETIME lastWriteTime;
35 } names_globals = {
36 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
36 + .spinlock = SPINLOCK_INITIALIZER,
37 .size = 0,
38 .array = NULL,
39 };
src/libnetdata/query_progress/progress.c
+1 -1
@@ -72,7 +72,7 @@ static struct progress {
72
73 } progress = {
74 .initialized = false,
75 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
75 + .spinlock = SPINLOCK_INITIALIZER,
76 };
77
78 SIMPLE_HASHTABLE_HASH query_hash(nd_uuid_t *transaction) {
src/libnetdata/socket/nd-poll.c
+8 -6
@@ -90,16 +90,18 @@ int nd_poll_wait(nd_poll_t *ndpl, int timeout_ms, nd_poll_result_t *result) {
90 int n = epoll_wait(ndpl->epoll_fd, &ndpl->ev[0], _countof(ndpl->ev), timeout_ms);
91
92 if(unlikely(n <= 0)) {
93 - if (n < 0) {
94 - result->events = ND_POLL_OTHER_ERROR;
95 - result->data = NULL;
96 - return -1;
97 - }
98 - else {
93 + if (n == 0) {
94 result->events = ND_POLL_TIMEOUT;
95 result->data = NULL;
96 return 0;
97 }
98 +
99 + if(errno == EINTR || errno == EAGAIN)
100 + continue;
101 +
102 + result->events = ND_POLL_OTHER_ERROR;
103 + result->data = NULL;
104 + return -1;
105 }
106
107 ndpl->used = n;
src/libnetdata/socket/nd-sock.h
-1
@@ -83,7 +83,6 @@ static inline ssize_t nd_sock_write(ND_SOCK *s, const void *buf, size_t num, siz
83 ssize_t rc;
84
85 do {
86 - errno_clear();
86 if (nd_sock_is_ssl(s))
87 rc = netdata_ssl_write(&s->ssl, buf, num);
88 else
src/libnetdata/socket/security.c
+1 -1
@@ -606,7 +606,7 @@ static SSL_CTX * netdata_ssl_create_server_ctx(unsigned long mode) {
606 * NETDATA_SSL_CONTEXT_EXPORTING - Starts the OpenTSDB context
607 */
608 void netdata_ssl_initialize_ctx(int selector) {
609 - static SPINLOCK sp = NETDATA_SPINLOCK_INITIALIZER;
609 + static SPINLOCK sp = SPINLOCK_INITIALIZER;
610 spinlock_lock(&sp);
611
612 switch (selector) {
src/libnetdata/spawn_server/spawn-tester.c
+2 -2
@@ -108,7 +108,7 @@ void test_int_fds_plugin_kill_to_stop(SPAWN_SERVER *server, const char *argv0) {
108 }
109 fprintf(stderr, "\n");
110
111 - int code = spawn_server_exec_kill(server, si);
111 + int code = spawn_server_exec_kill(server, si, 0);
112
113 nd_log(NDLS_COLLECTORS, NDLP_ERR,
114 "child exited with code %d",
@@ -162,7 +162,7 @@ void test_popen_plugin_kill_to_stop(const char *argv0) {
162 }
163 fprintf(stderr, "\n");
164
165 - int code = spawn_popen_kill(pi);
165 + int code = spawn_popen_kill(pi, 0);
166
167 nd_log(NDLS_COLLECTORS, NDLP_ERR,
168 "child exited with code %d",
src/libnetdata/spawn_server/spawn_popen.c
+3 -3
@@ -9,7 +9,7 @@ struct popen_instance {
9 };
10
11 SPAWN_SERVER *netdata_main_spawn_server = NULL;
12 -static SPINLOCK netdata_main_spawn_server_spinlock = NETDATA_SPINLOCK_INITIALIZER;
12 +static SPINLOCK netdata_main_spawn_server_spinlock = SPINLOCK_INITIALIZER;
13
14 bool netdata_main_spawn_server_init(const char *name, int argc, const char **argv) {
15 if(netdata_main_spawn_server == NULL) {
@@ -180,11 +180,11 @@ int spawn_popen_wait(POPEN_INSTANCE *pi) {
180 return spawn_popen_status_rc(status);
181 }
182
183 -int spawn_popen_kill(POPEN_INSTANCE *pi) {
183 +int spawn_popen_kill(POPEN_INSTANCE *pi, int timeout_ms) {
184 if(!pi) return -1;
185
186 spawn_popen_close_files(pi);
187 - int status = spawn_server_exec_kill(netdata_main_spawn_server, pi->si);
187 + int status = spawn_server_exec_kill(netdata_main_spawn_server, pi->si, timeout_ms);
188 freez(pi);
189 return spawn_popen_status_rc(status);
190 }
src/libnetdata/spawn_server/spawn_popen.h
+1 -1
@@ -15,7 +15,7 @@ POPEN_INSTANCE *spawn_popen_run(const char *cmd);
15 POPEN_INSTANCE *spawn_popen_run_argv(const char **argv);
16 POPEN_INSTANCE *spawn_popen_run_variadic(const char *cmd, ...);
17 int spawn_popen_wait(POPEN_INSTANCE *pi);
18 -int spawn_popen_kill(POPEN_INSTANCE *pi);
18 +int spawn_popen_kill(POPEN_INSTANCE *pi, int timeout_ms);
19
20 pid_t spawn_popen_pid(POPEN_INSTANCE *pi);
21 int spawn_popen_read_fd(POPEN_INSTANCE *pi);
src/libnetdata/spawn_server/spawn_server.h
+1 -1
@@ -42,7 +42,7 @@ void spawn_server_destroy(SPAWN_SERVER *server);
42 pid_t spawn_server_pid(SPAWN_SERVER *server);
43
44 SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custom_fd, const char **argv, const void *data, size_t data_size, SPAWN_INSTANCE_TYPE type);
45 -int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *si);
45 +int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *si, int timeout_ms);
46 int spawn_server_exec_wait(SPAWN_SERVER *server, SPAWN_INSTANCE *si);
47
48 int spawn_server_instance_read_fd(SPAWN_INSTANCE *si);
src/libnetdata/spawn_server/spawn_server_libuv.c
+1 -1
@@ -361,7 +361,7 @@ SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd __maybe_un
361 return item.instance;
362 }
363
364 -int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
364 +int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si, int timeout_ms __maybe_unused) {
365 if(!si) return -1;
366
367 // close all pipe descriptors to force the child to exit
src/libnetdata/spawn_server/spawn_server_nofork.c
+13 -2
@@ -1197,9 +1197,20 @@ int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *
1197 return rc;
1198 }
1199
1200 -int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *instance) {
1200 +int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *instance, int timeout_ms) {
1201 + if(instance->write_fd != -1) { close(instance->write_fd); instance->write_fd = -1; }
1202 + if(instance->read_fd != -1) { close(instance->read_fd); instance->read_fd = -1; }
1203 +
1204 + if(timeout_ms > 0) {
1205 + short revents;
1206 + NETDATA_SSL ssl = { 0 };
1207 + wait_on_socket_or_cancel_with_timeout(&ssl, instance->sock, timeout_ms, POLLIN, &revents);
1208 + }
1209 +
1210 // kill the child, if it is still running
1202 - if(instance->child_pid) kill(instance->child_pid, SIGTERM);
1211 + if(instance->child_pid)
1212 + kill(instance->child_pid, SIGTERM);
1213 +
1214 return spawn_server_exec_wait(server, instance);
1215 }
1216
src/libnetdata/spawn_server/spawn_server_posix.c
+3 -3
@@ -22,7 +22,7 @@ static struct {
22 SPINLOCK spinlock;
23 SPAWN_INSTANCE *instances;
24 } spawn_globals = {
25 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
25 + .spinlock = SPINLOCK_INITIALIZER,
26 .instances = NULL,
27 };
28
@@ -163,7 +163,7 @@ SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custo
163
164 // unfortunately, on CYGWIN/MSYS posix_spawn() is not thread safe
165 // so, we run it one by one.
166 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
166 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
167 spinlock_lock(&spinlock);
168
169 int fds[3] = { stdin_pipe[PIPE_READ], stdout_pipe[PIPE_WRITE], stderr_fd };
@@ -208,7 +208,7 @@ SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custo
208 return si;
209 }
210
211 -int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *si) {
211 +int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *si, int timeout_ms __maybe_unused) {
212 if (!si) return -1;
213
214 if (kill(si->child_pid, SIGTERM))
src/libnetdata/spawn_server/spawn_server_windows.c
+24 -18
@@ -144,7 +144,7 @@ int set_fd_blocking(int fd) {
144 //}
145
146 SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd __maybe_unused, int custom_fd __maybe_unused, const char **argv, const void *data __maybe_unused, size_t data_size __maybe_unused, SPAWN_INSTANCE_TYPE type) {
147 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
147 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
148
149 if (type != SPAWN_INSTANCE_TYPE_EXEC)
150 return NULL;
@@ -393,22 +393,20 @@ int map_status_code_to_signal(DWORD status_code) {
393 }
394 }
395
396 -int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
397 - if(si->child_pid != -1 && kill(si->child_pid, SIGTERM) != 0)
398 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
399 - "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), failed to be killed",
400 - si->request_id, (int)si->child_pid, si->dwProcessId);
401 -
396 +int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si, int timeout_ms __maybe_unused) {
397 // this gives some warnings at the spawn-tester, but it is generally better
398 // to have them, to avoid abnormal shutdown of the plugins
399 if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
400 if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
406 - if(si->stderr_fd != -1) {
407 - if(!log_forwarder_del_and_close_fd(server->log_forwarder, si->stderr_fd))
408 - close(si->stderr_fd);
401
410 - si->stderr_fd = -1;
411 - }
402 + if(timeout_ms > 0)
403 + WaitForSingleObject(si->process_handle, timeout_ms);
404 +
405 + errno_clear();
406 + if(si->child_pid != -1 && kill(si->child_pid, SIGTERM) != 0)
407 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
408 + "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), failed to be killed",
409 + si->request_id, (int)si->child_pid, si->dwProcessId);
410
411 errno_clear();
412 if(TerminateProcess(si->process_handle, STATUS_CONTROL_C_EXIT) == 0)
@@ -419,12 +417,6 @@ int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *
417 errno_clear();
418 TerminateChildProcesses(si);
419
422 - return spawn_server_exec_wait(server, si);
423 -}
424 -
425 -int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
426 - if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
427 - if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
420 if(si->stderr_fd != -1) {
421 if(!log_forwarder_del_and_close_fd(server->log_forwarder, si->stderr_fd))
422 close(si->stderr_fd);
@@ -432,6 +424,13 @@ int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *
424 si->stderr_fd = -1;
425 }
426
427 + return spawn_server_exec_wait(server, si);
428 +}
429 +
430 +int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
431 + if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
432 + if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
433 +
434 // wait for the process to end
435 WaitForSingleObject(si->process_handle, INFINITE);
436
@@ -449,6 +448,13 @@ int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *
448 if(err)
449 LocalFree(err);
450
451 + if(si->stderr_fd != -1) {
452 + if(!log_forwarder_del_and_close_fd(server->log_forwarder, si->stderr_fd))
453 + close(si->stderr_fd);
454 +
455 + si->stderr_fd = -1;
456 + }
457 +
458 freez(si);
459 return map_status_code_to_signal(exit_code);
460 }
src/libnetdata/threads/threads.c
+2 -2
@@ -53,11 +53,11 @@ static struct {
53 pthread_attr_t *attr;
54 } threads_globals = {
55 .exited = {
56 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
56 + .spinlock = SPINLOCK_INITIALIZER,
57 .list = NULL,
58 },
59 .running = {
60 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
60 + .spinlock = SPINLOCK_INITIALIZER,
61 .list = NULL,
62 },
63 .attr = NULL,
src/libnetdata/worker_utilization/worker_utilization.c
+97 -19
@@ -20,6 +20,15 @@ struct worker_job_type {
20 NETDATA_DOUBLE custom_value;
21 };
22
23 +struct worker_spinlock {
24 + const char *function;
25 + size_t locks;
26 + size_t spins;
27 +
28 + size_t statistics_last_locks;
29 + size_t statistics_last_spins;
30 +};
31 +
32 struct worker {
33 pid_t pid;
34 const char *tag;
@@ -40,6 +49,9 @@ struct worker {
49
50 struct worker_job_type per_job_type[WORKER_UTILIZATION_MAX_JOB_TYPES];
51
52 + size_t spinlocks_used;
53 + struct worker_spinlock spinlocks[WORKER_SPINLOCK_CONTENTION_FUNCTIONS];
54 +
55 struct worker *next;
56 struct worker *prev;
57 };
@@ -56,9 +68,9 @@ static struct workers_globals {
68 Pvoid_t worknames_JudyHS;
69 size_t memory;
70
59 -} workers_globals = { // workers globals, the base of all worknames
71 +} workers_globals = { // workers globals, the base of all worknames
72 .enabled = false,
61 - .spinlock = NETDATA_SPINLOCK_INITIALIZER, // a lock for the worknames index
73 + .spinlock = SPINLOCK_INITIALIZER, // a lock for the worknames index
74 .worknames_JudyHS = NULL, // the worknames index
75 };
76
@@ -239,30 +251,61 @@ void worker_set_metric(size_t job_id, NETDATA_DOUBLE value) {
251 }
252 }
253
254 +// --------------------------------------------------------------------------------------------------------------------
255 +
256 +static inline size_t pointer_hash_function(const char *func) {
257 + uintptr_t addr = (uintptr_t)func;
258 + return (size_t)(((addr >> 4) | (addr >> 16)) + func[0]) % WORKER_SPINLOCK_CONTENTION_FUNCTIONS;
259 +}
260 +
261 +void worker_spinlock_contention(const char *func, size_t spins) {
262 + if(unlikely(!worker))
263 + return;
264 +
265 + size_t hash = pointer_hash_function(func);
266 + for (size_t i = 0; i < WORKER_SPINLOCK_CONTENTION_FUNCTIONS; i++) {
267 + size_t slot = (hash + i) % WORKER_SPINLOCK_CONTENTION_FUNCTIONS;
268 + if (worker->spinlocks[slot].function == func || worker->spinlocks[slot].function == NULL) {
269 + // Either an empty slot or a matching slot
270 +
271 + worker->spinlocks[slot].function = func;
272 + worker->spinlocks[slot].locks++;
273 + worker->spinlocks[slot].spins += spins;
274 +
275 + return;
276 + }
277 + }
278 +
279 + // Array is full - do nothing
280 +}
281 +
282 // statistics interface
283
284 void workers_foreach(const char *name, void (*callback)(
245 - void *data
246 - , pid_t pid
247 - , const char *thread_tag
248 - , size_t max_job_id
249 - , size_t utilization_usec
250 - , size_t duration_usec
251 - , size_t jobs_started, size_t is_running
252 - , STRING **job_types_names
253 - , STRING **job_types_units
254 - , WORKER_METRIC_TYPE *job_metric_types
255 - , size_t *job_types_jobs_started
256 - , usec_t *job_types_busy_time
257 - , NETDATA_DOUBLE *job_custom_values
258 - )
259 - , void *data) {
285 + void *data
286 + , pid_t pid
287 + , const char *thread_tag
288 + , size_t max_job_id
289 + , size_t utilization_usec
290 + , size_t duration_usec
291 + , size_t jobs_started, size_t is_running
292 + , STRING **job_types_names
293 + , STRING **job_types_units
294 + , WORKER_METRIC_TYPE *job_metric_types
295 + , size_t *job_types_jobs_started
296 + , usec_t *job_types_busy_time
297 + , NETDATA_DOUBLE *job_custom_values
298 + , const char *spinlock_functions[]
299 + , size_t *spinlock_locks
300 + , size_t *spinlock_spins
301 + )
302 + , void *data) {
303 if(!workers_globals.enabled)
304 return;
305
306 spinlock_lock(&workers_globals.spinlock);
307 usec_t busy_time, delta;
265 - size_t i, jobs_started, jobs_running;
308 + size_t jobs_started, jobs_running;
309
310 size_t workname_size = strlen(name) + 1;
311 struct workers_workname *workname;
@@ -291,8 +334,12 @@ void workers_foreach(const char *name, void (*callback)(
334 usec_t per_job_type_busy_time[WORKER_UTILIZATION_MAX_JOB_TYPES];
335 NETDATA_DOUBLE per_job_custom_values[WORKER_UTILIZATION_MAX_JOB_TYPES];
336
337 + const char *spinlock_functions[WORKER_SPINLOCK_CONTENTION_FUNCTIONS];
338 + size_t spinlock_locks[WORKER_SPINLOCK_CONTENTION_FUNCTIONS];
339 + size_t spinlock_spins[WORKER_SPINLOCK_CONTENTION_FUNCTIONS];
340 +
341 size_t max_job_id = p->worker_max_job_id;
295 - for(i = 0; i <= max_job_id ;i++) {
342 + for(size_t i = 0; i <= max_job_id ;i++) {
343 per_job_type_name[i] = p->per_job_type[i].name;
344 per_job_type_units[i] = p->per_job_type[i].units;
345 per_job_metric_type[i] = p->per_job_type[i].type;
@@ -375,6 +422,34 @@ void workers_foreach(const char *name, void (*callback)(
422 jobs_running = 1;
423 }
424
425 + // ------------------------------------------------------------------------------------------------------------
426 + // spinlock contention
427 +
428 + size_t t = 0;
429 + for(size_t i = 0; i < WORKER_SPINLOCK_CONTENTION_FUNCTIONS ;i++) {
430 + if(!p->spinlocks[i].function) continue;
431 +
432 + spinlock_functions[t] = p->spinlocks[i].function;
433 +
434 + size_t tmp = p->spinlocks[i].locks;
435 + spinlock_locks[t] = tmp - p->spinlocks[i].statistics_last_locks;
436 + p->spinlocks[i].statistics_last_locks = tmp;
437 +
438 + tmp = p->spinlocks[i].spins;
439 + spinlock_spins[t] = tmp - p->spinlocks[i].statistics_last_spins;
440 + p->spinlocks[i].statistics_last_spins = tmp;
441 +
442 + t++;
443 + }
444 +
445 + for(; t < WORKER_SPINLOCK_CONTENTION_FUNCTIONS ;t++) {
446 + spinlock_functions[t] = NULL;
447 + spinlock_locks[t] = 0;
448 + spinlock_spins[t] = 0;
449 + }
450 +
451 + // ------------------------------------------------------------------------------------------------------------
452 +
453 callback(data
454 , p->pid
455 , p->tag
@@ -389,6 +464,9 @@ void workers_foreach(const char *name, void (*callback)(
464 , per_job_type_jobs_started
465 , per_job_type_busy_time
466 , per_job_custom_values
467 + , spinlock_functions
468 + , spinlock_locks
469 + , spinlock_spins
470 );
471 }
472
src/libnetdata/worker_utilization/worker_utilization.h
+21 -16
@@ -6,6 +6,7 @@
6 // workers interfaces
7
8 #define WORKER_UTILIZATION_MAX_JOB_TYPES 80
9 +#define WORKER_SPINLOCK_CONTENTION_FUNCTIONS 200
10
11 typedef enum __attribute__((packed)) {
12 WORKER_METRIC_EMPTY = 0,
@@ -25,25 +26,29 @@ void worker_unregister(void);
26 void worker_is_idle(void);
27 void worker_is_busy(size_t job_id);
28 void worker_set_metric(size_t job_id, NETDATA_DOUBLE value);
29 +void worker_spinlock_contention(const char *func, size_t spins);
30
31 // statistics interface
32
33 void workers_foreach(const char *name, void (*callback)(
32 - void *data
33 - , pid_t pid
34 - , const char *thread_tag
35 - , size_t max_job_id
36 - , size_t utilization_usec
37 - , size_t duration_usec
38 - , size_t jobs_started
39 - , size_t is_running
40 - , STRING **job_types_names
41 - , STRING **job_types_units
42 - , WORKER_METRIC_TYPE *job_metric_types
43 - , size_t *job_types_jobs_started
44 - , usec_t *job_types_busy_time
45 - , NETDATA_DOUBLE *job_custom_values
46 - )
47 - , void *data);
34 + void *data
35 + , pid_t pid
36 + , const char *thread_tag
37 + , size_t max_job_id
38 + , size_t utilization_usec
39 + , size_t duration_usec
40 + , size_t jobs_started
41 + , size_t is_running
42 + , STRING **job_types_names
43 + , STRING **job_types_units
44 + , WORKER_METRIC_TYPE *job_metric_types
45 + , size_t *job_types_jobs_started
46 + , usec_t *job_types_busy_time
47 + , NETDATA_DOUBLE *job_custom_values
48 + , const char *spinlock_functions[]
49 + , size_t *spinlock_locks
50 + , size_t *spinlock_spins
51 + )
52 + , void *data);
53
54 #endif // WORKER_UTILIZATION_H
src/ml/ad_charts.cc
+69 -22
@@ -289,7 +289,7 @@ void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number
289 rrdset_flag_set(host->type_anomaly_rate_rs, RRDSET_FLAG_ANOMALY_DETECTION);
290 }
291
292 - spinlock_lock_cancelable(&host->type_anomaly_rate_spinlock);
292 + spinlock_lock(&host->type_anomaly_rate_spinlock);
293 for (auto &entry : host->type_anomaly_rate) {
294 ml_type_anomaly_rate_t &type_anomaly_rate = entry.second;
295
@@ -306,7 +306,7 @@ void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number
306 type_anomaly_rate.anomalous_dimensions = 0;
307 type_anomaly_rate.normal_dimensions = 0;
308 }
309 - spinlock_unlock_cancelable(&host->type_anomaly_rate_spinlock);
309 + spinlock_unlock(&host->type_anomaly_rate_spinlock);
310
311 rrdset_done(host->type_anomaly_rate_rs);
312 }
@@ -409,8 +409,8 @@ void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_sta
409 char id_buf[1024];
410 char name_buf[1024];
411
412 - snprintfz(id_buf, 1024, "training_queue_%zu_stats", worker->id);
413 - snprintfz(name_buf, 1024, "training_queue_%zu_stats", worker->id);
412 + snprintfz(id_buf, 1024, "training_queue_%zu_ops", worker->id);
413 + snprintfz(name_buf, 1024, "training_queue_%zu_ops", worker->id);
414
415 worker->queue_stats_rs = rrdset_create(
416 localhost,
@@ -418,9 +418,9 @@ void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_sta
418 id_buf, // id
419 name_buf, // name
420 NETDATA_ML_CHART_FAMILY, // family
421 - "netdata.queue_stats", // ctx
422 - "Training queue stats", // title
423 - "items", // units
421 + "netdata.queue_ops", // ctx
422 + "Training queue operations", // title
423 + "count", // units
424 NETDATA_ML_PLUGIN, // plugin
425 NETDATA_ML_MODULE_TRAINING, // module
426 NETDATA_ML_CHART_PRIO_QUEUE_STATS, // priority
@@ -429,20 +429,67 @@ void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_sta
429 );
430 rrdset_flag_set(worker->queue_stats_rs, RRDSET_FLAG_ANOMALY_DETECTION);
431
432 - worker->queue_stats_queue_size_rd =
433 - rrddim_add(worker->queue_stats_rs, "queue_size", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
434 - worker->queue_stats_popped_items_rd =
435 - rrddim_add(worker->queue_stats_rs, "popped_items", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
432 + worker->queue_stats_num_create_new_model_requests_rd =
433 + rrddim_add(worker->queue_stats_rs, "pushed create model", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
434 + worker->queue_stats_num_create_new_model_requests_completed_rd =
435 + rrddim_add(worker->queue_stats_rs, "popped create model", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
436 +
437 + worker->queue_stats_num_add_existing_model_requests_rd =
438 + rrddim_add(worker->queue_stats_rs, "pushed add model", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
439 +
440 + worker->queue_stats_num_add_existing_model_requests_completed_rd =
441 + rrddim_add(worker->queue_stats_rs, "popped add models", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
442 }
443
444 rrddim_set_by_pointer(worker->queue_stats_rs,
439 - worker->queue_stats_queue_size_rd, stats.queue_size);
445 + worker->queue_stats_num_create_new_model_requests_rd, stats.total_create_new_model_requests_pushed);
446 rrddim_set_by_pointer(worker->queue_stats_rs,
441 - worker->queue_stats_popped_items_rd, stats.num_popped_items);
447 + worker->queue_stats_num_create_new_model_requests_completed_rd, stats.total_create_new_model_requests_popped);
448 +
449 + rrddim_set_by_pointer(worker->queue_stats_rs,
450 + worker->queue_stats_num_add_existing_model_requests_rd, stats.total_add_existing_model_requests_pushed);
451 + rrddim_set_by_pointer(worker->queue_stats_rs,
452 + worker->queue_stats_num_add_existing_model_requests_completed_rd, stats.total_add_existing_model_requests_popped);
453
454 rrdset_done(worker->queue_stats_rs);
455 }
456
457 + {
458 + if (!worker->queue_size_rs) {
459 + char id_buf[1024];
460 + char name_buf[1024];
461 +
462 + snprintfz(id_buf, 1024, "training_queue_%zu_size", worker->id);
463 + snprintfz(name_buf, 1024, "training_queue_%zu_size", worker->id);
464 +
465 + worker->queue_size_rs = rrdset_create(
466 + localhost,
467 + "netdata", // type
468 + id_buf, // id
469 + name_buf, // name
470 + NETDATA_ML_CHART_FAMILY, // family
471 + "netdata.queue_size", // ctx
472 + "Training queue size", // title
473 + "count", // units
474 + NETDATA_ML_PLUGIN, // plugin
475 + NETDATA_ML_MODULE_TRAINING, // module
476 + NETDATA_ML_CHART_PRIO_QUEUE_STATS, // priority
477 + localhost->rrd_update_every, // update_every
478 + RRDSET_TYPE_LINE// chart_type
479 + );
480 + rrdset_flag_set(worker->queue_size_rs, RRDSET_FLAG_ANOMALY_DETECTION);
481 +
482 + worker->queue_size_rd =
483 + rrddim_add(worker->queue_size_rs, "items", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
484 + }
485 +
486 + ml_queue_size_t qs = ml_queue_size(worker->queue);
487 + collected_number cn = qs.add_exisiting_model + qs.create_new_model;
488 +
489 + rrddim_set_by_pointer(worker->queue_size_rs, worker->queue_size_rd, cn);
490 + rrdset_done(worker->queue_size_rs);
491 + }
492 +
493 /*
494 * training stats
495 */
@@ -462,7 +509,7 @@ void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_sta
509 NETDATA_ML_CHART_FAMILY, // family
510 "netdata.training_time_stats", // ctx
511 "Training time stats", // title
465 - "milliseconds", // units
512 + "microseconds", // units
513 NETDATA_ML_PLUGIN, // plugin
514 NETDATA_ML_MODULE_TRAINING, // module
515 NETDATA_ML_CHART_PRIO_TRAINING_TIME_STATS, // priority
@@ -472,11 +519,11 @@ void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_sta
519 rrdset_flag_set(worker->training_time_stats_rs, RRDSET_FLAG_ANOMALY_DETECTION);
520
521 worker->training_time_stats_allotted_rd =
475 - rrddim_add(worker->training_time_stats_rs, "allotted", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
522 + rrddim_add(worker->training_time_stats_rs, "allotted", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
523 worker->training_time_stats_consumed_rd =
477 - rrddim_add(worker->training_time_stats_rs, "consumed", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
524 + rrddim_add(worker->training_time_stats_rs, "consumed", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
525 worker->training_time_stats_remaining_rd =
479 - rrddim_add(worker->training_time_stats_rs, "remaining", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
526 + rrddim_add(worker->training_time_stats_rs, "remaining", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
527 }
528
529 rrddim_set_by_pointer(worker->training_time_stats_rs,
@@ -518,15 +565,15 @@ void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_sta
565 rrdset_flag_set(worker->training_results_rs, RRDSET_FLAG_ANOMALY_DETECTION);
566
567 worker->training_results_ok_rd =
521 - rrddim_add(worker->training_results_rs, "ok", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
568 + rrddim_add(worker->training_results_rs, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
569 worker->training_results_invalid_query_time_range_rd =
523 - rrddim_add(worker->training_results_rs, "invalid-queries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
570 + rrddim_add(worker->training_results_rs, "invalid-queries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
571 worker->training_results_not_enough_collected_values_rd =
525 - rrddim_add(worker->training_results_rs, "not-enough-values", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
572 + rrddim_add(worker->training_results_rs, "not-enough-values", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
573 worker->training_results_null_acquired_dimension_rd =
527 - rrddim_add(worker->training_results_rs, "null-acquired-dimensions", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
574 + rrddim_add(worker->training_results_rs, "null-acquired-dimensions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
575 worker->training_results_chart_under_replication_rd =
529 - rrddim_add(worker->training_results_rs, "chart-under-replication", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
576 + rrddim_add(worker->training_results_rs, "chart-under-replication", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
577 }
578
579 rrddim_set_by_pointer(worker->training_results_rs,
src/ml/ml.cc
+58 -44
@@ -113,7 +113,7 @@ ml_dimension_calculated_numbers(ml_worker_t *worker, ml_dimension_t *dim, const
113 }
114 storage_engine_query_finalize(&handle);
115
116 - telemetry_queries_ml_query_completed(/* points_read */ idx);
116 + pulse_queries_ml_query_completed(/* points_read */ idx);
117
118 training_response.total_values = idx;
119 if (training_response.collected_values < min_n) {
@@ -443,7 +443,7 @@ static void ml_dimension_serialize_kmeans(const ml_dimension_t *dim, BUFFER *wb)
443 {
444 RRDDIM *rd = dim->rd;
445
446 - buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
446 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
447 buffer_json_member_add_string(wb, "version", "1");
448 buffer_json_member_add_string(wb, "machine-guid", rd->rrdset->rrdhost->machine_guid);
449 buffer_json_member_add_string(wb, "chart", rrdset_id(rd->rrdset));
@@ -550,7 +550,7 @@ ml_dimension_deserialize_kmeans(const char *json_str)
550
551 ml_dimension_t *Dim = reinterpret_cast<ml_dimension_t *>(AcqDim.dimension());
552 if (!Dim) {
553 - telemetry_ml_models_ignored();
553 + pulse_ml_models_ignored();
554 return true;
555 }
556
@@ -571,7 +571,9 @@ static void ml_dimension_stream_kmeans(const ml_dimension_t *dim)
571 if (!s)
572 return;
573
574 - if(!stream_sender_has_capabilities(dim->rd->rrdset->rrdhost, STREAM_CAP_ML_MODELS))
574 + if(!stream_sender_has_capabilities(dim->rd->rrdset->rrdhost, STREAM_CAP_ML_MODELS) ||
575 + !rrdset_check_upstream_exposed(dim->rd->rrdset) ||
576 + !rrddim_check_upstream_exposed(dim->rd))
577 return;
578
579 CLEAN_BUFFER *payload = buffer_create(0, NULL);
@@ -584,7 +586,7 @@ static void ml_dimension_stream_kmeans(const ml_dimension_t *dim)
586 buffer_tostring(payload));
587
588 sender_commit_clean_buffer(s, wb, STREAM_TRAFFIC_TYPE_METADATA);
587 - telemetry_ml_models_sent();
589 + pulse_ml_models_sent();
590 }
591
592 static void ml_dimension_update_models(ml_worker_t *worker, ml_dimension_t *dim)
@@ -830,7 +832,7 @@ ml_dimension_predict(ml_dimension_t *dim, time_t curr_time, calculated_number_t
832 continue;
833
834 if (anomaly_score < (100 * Cfg.dimension_anomaly_score_threshold)) {
833 - telemetry_ml_models_consulted(models_consulted);
835 + pulse_ml_models_consulted(models_consulted);
836 spinlock_unlock(&dim->slock);
837 return false;
838 }
@@ -847,7 +849,7 @@ ml_dimension_predict(ml_dimension_t *dim, time_t curr_time, calculated_number_t
849
850 spinlock_unlock(&dim->slock);
851
850 - telemetry_ml_models_consulted(models_consulted);
852 + pulse_ml_models_consulted(models_consulted);
853 return sum;
854 }
855
@@ -966,7 +968,7 @@ ml_host_detect_once(ml_host_t *host)
968 host->mls.num_anomalous_dimensions += chart_mls.num_anomalous_dimensions;
969 host->mls.num_normal_dimensions += chart_mls.num_normal_dimensions;
970
969 - if (spinlock_trylock_cancelable(&host->type_anomaly_rate_spinlock))
971 + if (spinlock_trylock(&host->type_anomaly_rate_spinlock))
972 {
973 STRING *key = rs->parts.type;
974 auto &um = host->type_anomaly_rate;
@@ -982,7 +984,7 @@ ml_host_detect_once(ml_host_t *host)
984
985 it->second.anomalous_dimensions += chart_mls.num_anomalous_dimensions;
986 it->second.normal_dimensions += chart_mls.num_normal_dimensions;
985 - spinlock_unlock_cancelable(&host->type_anomaly_rate_spinlock);
987 + spinlock_unlock(&host->type_anomaly_rate_spinlock);
988 }
989 }
990 rrdset_foreach_done(rsp);
@@ -1053,27 +1055,8 @@ ml_detect_main(void *arg)
1055
1056 netdata_mutex_lock(&worker->nd_mutex);
1057 ml_queue_stats_t queue_stats = worker->queue_stats;
1056 - worker->queue_stats = {};
1058 netdata_mutex_unlock(&worker->nd_mutex);
1059
1059 - // calc the avg values
1060 - if (queue_stats.num_popped_items) {
1061 - queue_stats.queue_size /= queue_stats.num_popped_items;
1062 - queue_stats.allotted_ut /= queue_stats.num_popped_items;
1063 - queue_stats.consumed_ut /= queue_stats.num_popped_items;
1064 - queue_stats.remaining_ut /= queue_stats.num_popped_items;
1065 - } else {
1066 - queue_stats.queue_size = ml_queue_size(worker->queue);
1067 - queue_stats.consumed_ut = 0;
1068 - queue_stats.remaining_ut = queue_stats.allotted_ut;
1069 -
1070 - queue_stats.item_result_ok = 0;
1071 - queue_stats.item_result_invalid_query_time_range = 0;
1072 - queue_stats.item_result_not_enough_collected_values = 0;
1073 - queue_stats.item_result_null_acquired_dimension = 0;
1074 - queue_stats.item_result_chart_under_replication = 0;
1075 - }
1076 -
1060 ml_update_training_statistics_chart(worker, queue_stats);
1061 }
1062 }
@@ -1163,13 +1146,13 @@ static enum ml_worker_result ml_worker_add_existing_model(ml_worker_t *worker, m
1146
1147 ml_dimension_t *Dim = reinterpret_cast<ml_dimension_t *>(AcqDim.dimension());
1148 if (!Dim) {
1166 - telemetry_ml_models_ignored();
1149 + pulse_ml_models_ignored();
1150 return ML_WORKER_RESULT_OK;
1151 }
1152
1153 Dim->kmeans = req.inlined_km;
1154 ml_dimension_update_models(worker, Dim);
1172 - telemetry_ml_models_received();
1155 + pulse_ml_models_received();
1156 return ML_WORKER_RESULT_OK;
1157 }
1158
@@ -1192,14 +1175,16 @@ void *ml_train_main(void *arg) {
1175 while (!Cfg.training_stop) {
1176 worker_is_busy(WORKER_TRAIN_QUEUE_POP);
1177
1178 + ml_queue_stats_t loop_stats{};
1179 +
1180 ml_queue_item_t item = ml_queue_pop(worker->queue);
1181 if (item.type == ML_QUEUE_ITEM_STOP_REQUEST) {
1182 break;
1183 }
1184
1200 - size_t queue_size = ml_queue_size(worker->queue) + 1;
1185 + ml_queue_size_t queue_size = ml_queue_size(worker->queue);
1186
1202 - usec_t allotted_ut = (Cfg.train_every * USEC_PER_SEC) / queue_size;
1187 + usec_t allotted_ut = (Cfg.train_every * USEC_PER_SEC) / (queue_size.create_new_model + 1);
1188 if (allotted_ut > USEC_PER_SEC)
1189 allotted_ut = USEC_PER_SEC;
1190
@@ -1230,44 +1215,73 @@ void *ml_train_main(void *arg) {
1215 if (Cfg.enable_statistics_charts) {
1216 worker_is_busy(WORKER_TRAIN_UPDATE_HOST);
1217
1233 - netdata_mutex_lock(&worker->nd_mutex);
1218 + ml_queue_stats_t queue_stats = ml_queue_stats(worker->queue);
1219
1235 - worker->queue_stats.queue_size += queue_size;
1236 - worker->queue_stats.num_popped_items += 1;
1220 + loop_stats.total_add_existing_model_requests_pushed = queue_stats.total_add_existing_model_requests_pushed;
1221 + loop_stats.total_add_existing_model_requests_popped = queue_stats.total_add_existing_model_requests_popped;
1222 + loop_stats.total_create_new_model_requests_pushed = queue_stats.total_create_new_model_requests_pushed;
1223 + loop_stats.total_create_new_model_requests_popped = queue_stats.total_create_new_model_requests_popped;
1224
1238 - worker->queue_stats.allotted_ut += allotted_ut;
1239 - worker->queue_stats.consumed_ut += consumed_ut;
1240 - worker->queue_stats.remaining_ut += remaining_ut;
1225 + loop_stats.allotted_ut = allotted_ut;
1226 + loop_stats.consumed_ut = consumed_ut;
1227 + loop_stats.remaining_ut = remaining_ut;
1228
1229 switch (worker_res) {
1230 case ML_WORKER_RESULT_OK:
1244 - worker->queue_stats.item_result_ok += 1;
1231 + loop_stats.item_result_ok = 1;
1232 break;
1233 case ML_WORKER_RESULT_INVALID_QUERY_TIME_RANGE:
1247 - worker->queue_stats.item_result_invalid_query_time_range += 1;
1234 + loop_stats.item_result_invalid_query_time_range = 1;
1235 break;
1236 case ML_WORKER_RESULT_NOT_ENOUGH_COLLECTED_VALUES:
1250 - worker->queue_stats.item_result_not_enough_collected_values += 1;
1237 + loop_stats.item_result_not_enough_collected_values = 1;
1238 break;
1239 case ML_WORKER_RESULT_NULL_ACQUIRED_DIMENSION:
1253 - worker->queue_stats.item_result_null_acquired_dimension += 1;
1240 + loop_stats.item_result_null_acquired_dimension = 1;
1241 break;
1242 case ML_WORKER_RESULT_CHART_UNDER_REPLICATION:
1256 - worker->queue_stats.item_result_chart_under_replication += 1;
1243 + loop_stats.item_result_chart_under_replication = 1;
1244 break;
1245 }
1246
1247 + netdata_mutex_lock(&worker->nd_mutex);
1248 +
1249 + worker->queue_stats.total_add_existing_model_requests_pushed = loop_stats.total_add_existing_model_requests_pushed;
1250 + worker->queue_stats.total_add_existing_model_requests_popped = loop_stats.total_add_existing_model_requests_popped;
1251 +
1252 + worker->queue_stats.total_create_new_model_requests_pushed = loop_stats.total_create_new_model_requests_pushed;
1253 + worker->queue_stats.total_create_new_model_requests_popped = loop_stats.total_create_new_model_requests_popped;
1254 +
1255 + worker->queue_stats.allotted_ut += loop_stats.allotted_ut;
1256 + worker->queue_stats.consumed_ut += loop_stats.consumed_ut;
1257 + worker->queue_stats.remaining_ut += loop_stats.remaining_ut;
1258 +
1259 + worker->queue_stats.item_result_ok += loop_stats.item_result_ok;
1260 + worker->queue_stats.item_result_invalid_query_time_range += loop_stats.item_result_invalid_query_time_range;
1261 + worker->queue_stats.item_result_not_enough_collected_values += loop_stats.item_result_not_enough_collected_values;
1262 + worker->queue_stats.item_result_null_acquired_dimension += loop_stats.item_result_null_acquired_dimension;
1263 + worker->queue_stats.item_result_chart_under_replication += loop_stats.item_result_chart_under_replication;
1264 +
1265 netdata_mutex_unlock(&worker->nd_mutex);
1266 }
1267
1268 + bool should_sleep = true;
1269 +
1270 if (worker->pending_model_info.size() >= Cfg.flush_models_batch_size) {
1271 worker_is_busy(WORKER_TRAIN_FLUSH_MODELS);
1272 netdata_mutex_lock(&db_mutex);
1273 ml_flush_pending_models(worker);
1274 netdata_mutex_unlock(&db_mutex);
1268 - continue;
1275 + should_sleep = false;
1276 }
1277
1278 + if (item.type == ML_QUEUE_ITEM_TYPE_ADD_EXISTING_MODEL) {
1279 + should_sleep = false;
1280 + }
1281 +
1282 + if (!should_sleep)
1283 + continue;
1284 +
1285 worker_is_idle();
1286 std::this_thread::sleep_for(std::chrono::microseconds{remaining_ut});
1287 }
src/ml/ml_memory.cc
+5 -5
@@ -1,7 +1,7 @@
1 #include <cstdlib>
2 #include <memory>
3
4 -#include "daemon/telemetry/telemetry-ml.h"
4 +#include "daemon/pulse/pulse-ml.h"
5
6 void *operator new(size_t size)
7 {
@@ -9,7 +9,7 @@ void *operator new(size_t size)
9 if (!ptr)
10 throw std::bad_alloc();
11
12 - telemetry_ml_memory_allocated(size);
12 + pulse_ml_memory_allocated(size);
13 return ptr;
14 }
15
@@ -19,14 +19,14 @@ void *operator new[](size_t size)
19 if (!ptr)
20 throw std::bad_alloc();
21
22 - telemetry_ml_memory_allocated(size);
22 + pulse_ml_memory_allocated(size);
23 return ptr;
24 }
25
26 void operator delete(void *ptr, size_t size) noexcept
27 {
28 if (ptr) {
29 - telemetry_ml_memory_freed(size);
29 + pulse_ml_memory_freed(size);
30 free(ptr);
31 }
32 }
@@ -34,7 +34,7 @@ void operator delete(void *ptr, size_t size) noexcept
34 void operator delete[](void *ptr, size_t size) noexcept
35 {
36 if (ptr) {
37 - telemetry_ml_memory_freed(size);
37 + pulse_ml_memory_freed(size);
38 free(ptr);
39 }
40 }
src/ml/ml_queue.cc
+48 -8
@@ -1,8 +1,9 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 +#include "ml/ml_queue.h"
4 #include "ml_private.h"
5
5 -ml_queue_t * ml_queue_init()
6 +ml_queue_t *ml_queue_init()
7 {
8 ml_queue_t *q = new ml_queue_t();
9
@@ -22,7 +23,23 @@ void ml_queue_destroy(ml_queue_t *q)
23 void ml_queue_push(ml_queue_t *q, const ml_queue_item_t req)
24 {
25 netdata_mutex_lock(&q->mutex);
25 - q->internal.push(req);
26 +
27 + switch (req.type) {
28 + case ML_QUEUE_ITEM_TYPE_CREATE_NEW_MODEL:
29 + q->create_model_queue.push(req.create_new_model);
30 + q->stats.total_create_new_model_requests_pushed += 1;
31 + break;
32 +
33 + case ML_QUEUE_ITEM_TYPE_ADD_EXISTING_MODEL:
34 + q->add_model_queue.push(req.add_existing_model);
35 + q->stats.total_add_existing_model_requests_pushed += 1;
36 + break;
37 +
38 + case ML_QUEUE_ITEM_STOP_REQUEST:
39 + // Stop requests don't need to be queued
40 + break;
41 + }
42 +
43 pthread_cond_signal(&q->cond_var);
44 netdata_mutex_unlock(&q->mutex);
45 }
@@ -34,7 +51,7 @@ ml_queue_item_t ml_queue_pop(ml_queue_t *q)
51 ml_queue_item_t req;
52 req.type = ML_QUEUE_ITEM_STOP_REQUEST;
53
37 - while (q->internal.empty()) {
54 + while (q->create_model_queue.empty() && q->add_model_queue.empty()) {
55 pthread_cond_wait(&q->cond_var, &q->mutex);
56
57 if (q->exit) {
@@ -43,19 +60,33 @@ ml_queue_item_t ml_queue_pop(ml_queue_t *q)
60 }
61 }
62
46 - req = q->internal.front();
47 - q->internal.pop();
63 + // Prioritize adding model requests
64 + if (!q->add_model_queue.empty()) {
65 + req.type = ML_QUEUE_ITEM_TYPE_ADD_EXISTING_MODEL;
66 + req.add_existing_model = q->add_model_queue.front();
67 + q->add_model_queue.pop();
68 + q->stats.total_add_existing_model_requests_popped += 1;
69 + } else if (!q->create_model_queue.empty()) {
70 + req.type = ML_QUEUE_ITEM_TYPE_CREATE_NEW_MODEL;
71 + req.create_new_model = q->create_model_queue.front();
72 + q->create_model_queue.pop();
73 + q->stats.total_create_new_model_requests_popped += 1;
74 + }
75
76 netdata_mutex_unlock(&q->mutex);
77 return req;
78 }
79
53 -size_t ml_queue_size(ml_queue_t *q)
80 +ml_queue_size_t ml_queue_size(ml_queue_t *q)
81 {
82 netdata_mutex_lock(&q->mutex);
56 - size_t size = q->internal.size();
83 + ml_queue_size_t qs = ml_queue_size_t {
84 + q->create_model_queue.size(),
85 + q->add_model_queue.size(),
86 + };
87 netdata_mutex_unlock(&q->mutex);
58 - return size;
88 +
89 + return qs;
90 }
91
92 void ml_queue_signal(ml_queue_t *q)
@@ -65,3 +96,12 @@ void ml_queue_signal(ml_queue_t *q)
96 pthread_cond_signal(&q->cond_var);
97 netdata_mutex_unlock(&q->mutex);
98 }
99 +
100 +ml_queue_stats_t ml_queue_stats(ml_queue_t *q)
101 +{
102 + netdata_mutex_lock(&q->mutex);
103 + ml_queue_stats_t stats = q->stats;
104 + netdata_mutex_unlock(&q->mutex);
105 +
106 + return stats;
107 +}
src/ml/ml_queue.h
+22 -9
@@ -32,16 +32,17 @@ typedef struct ml_queue_item {
32 ml_request_add_existing_model add_existing_model;
33 } ml_queue_item_t;
34
35 -struct ml_queue_t {
36 - std::queue<ml_queue_item_t> internal;
37 - netdata_mutex_t mutex;
38 - pthread_cond_t cond_var;
39 - std::atomic<bool> exit;
40 -};
35 +typedef struct {
36 + size_t create_new_model;
37 + size_t add_exisiting_model;
38 +} ml_queue_size_t;
39
40 typedef struct {
43 - size_t queue_size;
44 - size_t num_popped_items;
41 + size_t total_create_new_model_requests_pushed;
42 + size_t total_create_new_model_requests_popped;
43 +
44 + size_t total_add_existing_model_requests_pushed;
45 + size_t total_add_existing_model_requests_popped;
46
47 usec_t allotted_ut;
48 usec_t consumed_ut;
@@ -54,6 +55,16 @@ typedef struct {
55 size_t item_result_chart_under_replication;
56 } ml_queue_stats_t;
57
58 +struct ml_queue_t {
59 + std::queue<ml_request_add_existing_model_t> add_model_queue;
60 + std::queue<ml_request_create_new_model_t> create_model_queue;
61 + ml_queue_stats_t stats;
62 +
63 + netdata_mutex_t mutex;
64 + pthread_cond_t cond_var;
65 + std::atomic<bool> exit;
66 +};
67 +
68 ml_queue_t *ml_queue_init();
69
70 void ml_queue_destroy(ml_queue_t *q);
@@ -62,7 +73,9 @@ void ml_queue_push(ml_queue_t *q, const ml_queue_item_t req);
73
74 ml_queue_item_t ml_queue_pop(ml_queue_t *q);
75
65 -size_t ml_queue_size(ml_queue_t *q);
76 +ml_queue_size_t ml_queue_size(ml_queue_t *q);
77 +
78 +ml_queue_stats_t ml_queue_stats(ml_queue_t *q);
79
80 void ml_queue_signal(ml_queue_t *q);
81
src/ml/ml_worker.h
+7 -2
@@ -25,8 +25,13 @@ typedef struct {
25 std::vector<ml_model_info_t> pending_model_info;
26
27 RRDSET *queue_stats_rs;
28 - RRDDIM *queue_stats_queue_size_rd;
29 - RRDDIM *queue_stats_popped_items_rd;
28 + RRDDIM *queue_stats_num_create_new_model_requests_rd;
29 + RRDDIM *queue_stats_num_add_existing_model_requests_rd;
30 + RRDDIM *queue_stats_num_create_new_model_requests_completed_rd;
31 + RRDDIM *queue_stats_num_add_existing_model_requests_completed_rd;
32 +
33 + RRDSET *queue_size_rs;
34 + RRDDIM *queue_size_rd;
35
36 RRDSET *training_time_stats_rs;
37 RRDDIM *training_time_stats_allotted_rd;
src/plugins.d/plugins_d.c
+45 -25
@@ -76,7 +76,7 @@ static void pluginsd_worker_thread_cleanup(void *pptr) {
76 spinlock_unlock(&cd->unsafe.spinlock);
77
78 if (pi)
79 - spawn_popen_kill(pi);
79 + spawn_popen_kill(pi, 3 * MSEC_PER_SEC);
80 }
81
82 #define SERIAL_FAILURES_THRESHOLD 10
@@ -88,7 +88,7 @@ static void pluginsd_worker_thread_handle_success(struct plugind *cd) {
88
89 if (likely(cd->serial_failures <= SERIAL_FAILURES_THRESHOLD)) {
90 netdata_log_info("PLUGINSD: 'host:%s', '%s' (pid %d) does not generate useful output but it reports success (exits with 0). %s.",
91 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid,
91 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid,
92 plugin_is_enabled(cd) ? "Waiting a bit before starting it again." : "Will not start it again - it is now disabled.");
93
94 pluginsd_sleep(cd->update_every * 10);
@@ -99,7 +99,7 @@ static void pluginsd_worker_thread_handle_success(struct plugind *cd) {
99 netdata_log_error("PLUGINSD: 'host:'%s', '%s' (pid %d) does not generate useful output, "
100 "although it reports success (exits with 0)."
101 "We have tried to collect something %zu times - unsuccessfully. Disabling it.",
102 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid, cd->serial_failures);
102 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid, cd->serial_failures);
103 plugin_set_disabled(cd);
104 return;
105 }
@@ -108,21 +108,21 @@ static void pluginsd_worker_thread_handle_success(struct plugind *cd) {
108 static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_ret_code) {
109 if (worker_ret_code == -1) {
110 netdata_log_info("PLUGINSD: 'host:%s', '%s' (pid %d) was killed with SIGTERM. Disabling it.",
111 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid);
111 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid);
112 plugin_set_disabled(cd);
113 return;
114 }
115
116 if (!cd->successful_collections) {
117 netdata_log_error("PLUGINSD: 'host:%s', '%s' (pid %d) exited with error code %d and haven't collected any data. Disabling it.",
118 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid, worker_ret_code);
118 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid, worker_ret_code);
119 plugin_set_disabled(cd);
120 return;
121 }
122
123 if (cd->serial_failures <= SERIAL_FAILURES_THRESHOLD) {
124 netdata_log_error("PLUGINSD: 'host:%s', '%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times). %s",
125 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid, worker_ret_code, cd->successful_collections,
125 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid, worker_ret_code, cd->successful_collections,
126 plugin_is_enabled(cd) ? "Waiting a bit before starting it again." : "Will not start it again - it is disabled.");
127
128 pluginsd_sleep(cd->update_every * 10);
@@ -132,7 +132,7 @@ static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_r
132 if (cd->serial_failures > SERIAL_FAILURES_THRESHOLD) {
133 netdata_log_error("PLUGINSD: 'host:%s', '%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times)."
134 "We tried to restart it %zu times, but it failed to generate data. Disabling it.",
135 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid, worker_ret_code,
135 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid, worker_ret_code,
136 cd->successful_collections, cd->serial_failures);
137 plugin_set_disabled(cd);
138 return;
@@ -152,10 +152,10 @@ static void *pluginsd_worker_thread(void *arg) {
152 size_t count = 0;
153
154 while(service_running(SERVICE_COLLECTORS)) {
155 - cd->unsafe.pi = spawn_popen_run(cd->cmd);
155 + cd->unsafe.pi = spawn_popen_run(string2str(cd->cmd));
156 if(!cd->unsafe.pi) {
157 netdata_log_error("PLUGINSD: 'host:%s', cannot popen(\"%s\", \"r\").",
158 - rrdhost_hostname(cd->host), cd->cmd);
158 + rrdhost_hostname(cd->host), string2str(cd->cmd));
159 break;
160 }
161 cd->unsafe.pid = spawn_popen_pid(cd->unsafe.pi);
@@ -163,13 +163,13 @@ static void *pluginsd_worker_thread(void *arg) {
163 nd_log(NDLS_DAEMON, NDLP_DEBUG,
164 "PLUGINSD: 'host:%s' connected to '%s' running on pid %d",
165 rrdhost_hostname(cd->host),
166 - cd->fullfilename, cd->unsafe.pid);
166 + string2str(cd->fullfilename), cd->unsafe.pid);
167
168 - const char *plugin = strrchr(cd->fullfilename, '/');
168 + const char *plugin = strrchr(string2str(cd->fullfilename), '/');
169 if(plugin)
170 plugin++;
171 else
172 - plugin = cd->fullfilename;
172 + plugin = string2str(cd->fullfilename);
173
174 char module[100];
175 snprintfz(module, sizeof(module), "plugins.d[%s]", plugin);
@@ -188,9 +188,9 @@ static void *pluginsd_worker_thread(void *arg) {
188
189 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
190 "PLUGINSD: 'host:%s', '%s' (pid %d) disconnected after %zu successful data collections.",
191 - rrdhost_hostname(cd->host), cd->fullfilename, cd->unsafe.pid, count);
191 + rrdhost_hostname(cd->host), string2str(cd->fullfilename), cd->unsafe.pid, count);
192
193 - int worker_ret_code = spawn_popen_kill(cd->unsafe.pi);
193 + int worker_ret_code = spawn_popen_kill(cd->unsafe.pi, 3 * MSEC_PER_SEC);
194 cd->unsafe.pi = NULL;
195
196 if(likely(worker_ret_code == 0))
@@ -218,7 +218,7 @@ static void pluginsd_main_cleanup(void *pptr) {
218 spinlock_lock(&cd->unsafe.spinlock);
219 if (cd->unsafe.enabled && cd->unsafe.running && cd->unsafe.thread != 0) {
220 netdata_log_info("PLUGINSD: 'host:%s', stopping plugin thread: %s",
221 - rrdhost_hostname(cd->host), cd->id);
221 + rrdhost_hostname(cd->host), string2str(cd->id));
222
223 nd_thread_signal_cancel(cd->unsafe.thread);
224 }
@@ -321,11 +321,11 @@ void *pluginsd_main(void *ptr) {
321 // check if it runs already
322 struct plugind *cd;
323 for (cd = pluginsd_root; cd; cd = cd->next)
324 - if (unlikely(strcmp(cd->filename, file->d_name) == 0))
324 + if (unlikely(strcmp(string2str(cd->filename), file->d_name) == 0))
325 break;
326
327 if (likely(cd && plugin_is_running(cd))) {
328 - netdata_log_debug(D_PLUGINSD, "plugin '%s' is already running", cd->filename);
328 + netdata_log_debug(D_PLUGINSD, "plugin '%s' is already running", string2str(cd->filename));
329 continue;
330 }
331
@@ -334,22 +334,42 @@ void *pluginsd_main(void *ptr) {
334 if (unlikely(!cd)) {
335 cd = callocz(sizeof(struct plugind), 1);
336
337 - snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
337 + {
338 + char buf[CONFIG_MAX_NAME];
339 + snprintfz(buf, sizeof(buf), "plugin:%s", pluginname);
340 + string_freez(cd->id);
341 + cd->id = string_strdupz(buf);
342 + }
343 +
344 + {
345 + char buf[FILENAME_MAX + 1];
346 + strncpyz(buf, file->d_name, sizeof(buf) - 1);
347 + string_freez(cd->filename);
348 + cd->filename = string_strdupz(buf);
349
339 - strncpyz(cd->filename, file->d_name, FILENAME_MAX);
340 - snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", directory_name, cd->filename);
350 + snprintfz(buf, sizeof(buf), "%s/%s", directory_name, string2str(cd->filename));
351 + string_freez(cd->fullfilename);
352 + cd->fullfilename = string_strdupz(buf);
353 + }
354
355 cd->host = localhost;
356 cd->unsafe.enabled = enabled;
357 cd->unsafe.running = false;
358
346 - cd->update_every = (int)config_get_duration_seconds(cd->id, "update every", localhost->rrd_update_every);
359 + cd->update_every = (int)config_get_duration_seconds(string2str(cd->id), "update every", localhost->rrd_update_every);
360 cd->started_t = now_realtime_sec();
361
349 - char *def = "";
350 - snprintfz(
351 - cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every,
352 - config_get(cd->id, "command options", def));
362 + {
363 + const char *def = "";
364 + char buf[PLUGINSD_CMD_MAX + 1];
365 +
366 + snprintfz(
367 + buf, sizeof(buf), "exec %s %d %s", string2str(cd->fullfilename),
368 + cd->update_every, config_get(string2str(cd->id), "command options", def));
369 +
370 + string_freez(cd->cmd);
371 + cd->cmd = string_strdupz(buf);
372 + }
373
374 // link it
375 DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(pluginsd_root, cd, prev, next);
src/plugins.d/plugins_d.h
+5 -6
@@ -14,11 +14,10 @@ struct rrdhost;
14 extern char *plugin_directories[PLUGINSD_MAX_DIRECTORIES];
15
16 struct plugind {
17 - char id[CONFIG_MAX_NAME+1]; // config node id
18 -
19 - char filename[FILENAME_MAX+1]; // just the filename
20 - char fullfilename[FILENAME_MAX+1]; // with path
21 - char cmd[PLUGINSD_CMD_MAX+1]; // the command that it executes
17 + STRING *id; // config node id
18 + STRING *filename; // just the filename
19 + STRING *fullfilename; // with path
20 + STRING *cmd; // the command that it executes
21
22 size_t successful_collections; // the number of times we have seen
23 // values collected from this plugin
@@ -26,7 +25,7 @@ struct plugind {
25 size_t serial_failures; // the number of times the plugin started
26 // without collecting values
27
29 - struct rrdhost *host; // the host the plugin collects data for
28 + struct rrdhost *host; // the host the plugin collects data for
29 int update_every; // the plugin default data collection frequency
30
31 struct {
src/plugins.d/pluginsd_functions.c
+3 -3
@@ -51,7 +51,7 @@ static void inflight_functions_insert_callback(const DICTIONARY_ITEM *item, void
51
52 // send the command to the plugin
53 // IMPORTANT: make sure all commands are sent in 1 call, because in streaming they may interfere with others
54 - ssize_t ret = send_to_plugin(buffer_tostring(buffer), parser);
54 + ssize_t ret = send_to_plugin(buffer_tostring(buffer), parser, STREAM_TRAFFIC_TYPE_FUNCTIONS);
55 pf->sent_monotonic_ut = now_monotonic_usec();
56
57 if(ret < 0) {
@@ -155,7 +155,7 @@ static void pluginsd_function_cancel(void *data) {
155 snprintfz(buffer, sizeof(buffer), PLUGINSD_CALL_FUNCTION_CANCEL " %s\n", transaction);
156
157 // send the command to the plugin
158 - ssize_t ret = send_to_plugin(buffer, t->parser);
158 + ssize_t ret = send_to_plugin(buffer, t->parser, STREAM_TRAFFIC_TYPE_FUNCTIONS);
159 if(ret < 0)
160 sent = true;
161
@@ -183,7 +183,7 @@ static void pluginsd_function_progress_to_plugin(void *data) {
183 snprintfz(buffer, sizeof(buffer), PLUGINSD_CALL_FUNCTION_PROGRESS " %s\n", transaction);
184
185 // send the command to the plugin
186 - ssize_t ret = send_to_plugin(buffer, t->parser);
186 + ssize_t ret = send_to_plugin(buffer, t->parser, STREAM_TRAFFIC_TYPE_FUNCTIONS);
187 if(ret < 0)
188 sent = true;
189
src/plugins.d/pluginsd_internals.c
+4 -1
@@ -2,10 +2,13 @@
2
3 #include "pluginsd_internals.h"
4
5 -ssize_t send_to_plugin(const char *txt, PARSER *parser) {
5 +ssize_t send_to_plugin(const char *txt, PARSER *parser, STREAM_TRAFFIC_TYPE type) {
6 if(!txt || !*txt || !parser)
7 return 0;
8
9 + if(parser->send_to_plugin_cb)
10 + return parser->send_to_plugin_cb(txt, parser->send_to_plugin_data, type);
11 +
12 #ifdef ENABLE_H2O
13 if(parser->h2o_ctx)
14 return h2o_stream_write(parser->h2o_ctx, txt, strlen(txt));
src/plugins.d/pluginsd_internals.h
+11 -8
@@ -13,7 +13,7 @@
13
14 PARSER_RC PLUGINSD_DISABLE_PLUGIN(PARSER *parser, const char *keyword, const char *msg);
15
16 -ssize_t send_to_plugin(const char *txt, PARSER *parser);
16 +ssize_t send_to_plugin(const char *txt, PARSER *parser, STREAM_TRAFFIC_TYPE type);
17
18 static inline RRDHOST *pluginsd_require_scope_host(PARSER *parser, const char *cmd) {
19 RRDHOST *host = parser->user.host;
@@ -37,16 +37,16 @@ static inline RRDSET *pluginsd_get_scope_chart(PARSER *parser) {
37 return parser->user.st;
38 }
39
40 -static inline void pluginsd_lock_rrdset_data_collection(PARSER *parser) {
40 +static inline void rrdset_data_collection_lock_with_trace(PARSER *parser, const char *func) {
41 if(parser->user.st && !parser->user.v2.locked_data_collection) {
42 - spinlock_lock(&parser->user.st->data_collection_lock);
42 + spinlock_lock_with_trace(&parser->user.st->data_collection_lock, func);
43 parser->user.v2.locked_data_collection = true;
44 }
45 }
46
47 -static inline bool pluginsd_unlock_rrdset_data_collection(PARSER *parser) {
47 +static inline bool rrdset_data_collection_unlock_with_trace(PARSER *parser, const char *func) {
48 if(parser->user.st && parser->user.v2.locked_data_collection) {
49 - spinlock_unlock(&parser->user.st->data_collection_lock);
49 + spinlock_unlock_with_trace(&parser->user.st->data_collection_lock, func);
50 parser->user.v2.locked_data_collection = false;
51 return true;
52 }
@@ -54,8 +54,11 @@ static inline bool pluginsd_unlock_rrdset_data_collection(PARSER *parser) {
54 return false;
55 }
56
57 -static inline void pluginsd_unlock_previous_scope_chart(PARSER *parser, const char *keyword, bool stale) {
58 - if(unlikely(pluginsd_unlock_rrdset_data_collection(parser))) {
57 +#define rrdset_data_collection_lock(parser) rrdset_data_collection_lock_with_trace(parser, __FUNCTION__)
58 +#define rrdset_data_collection_unlock(parser) rrdset_data_collection_unlock_with_trace(parser, __FUNCTION__)
59 +
60 +static inline void rrdset_previous_scope_chart_unlock(PARSER *parser, const char *keyword, bool stale) {
61 + if(unlikely(rrdset_data_collection_unlock(parser))) {
62 if(stale)
63 netdata_log_error("PLUGINSD: 'host:%s/chart:%s/' stale data collection lock found during %s; it has been unlocked",
64 rrdhost_hostname(parser->user.st->rrdhost),
@@ -76,7 +79,7 @@ static inline void pluginsd_unlock_previous_scope_chart(PARSER *parser, const ch
79 }
80
81 static inline void pluginsd_clear_scope_chart(PARSER *parser, const char *keyword) {
79 - pluginsd_unlock_previous_scope_chart(parser, keyword, true);
82 + rrdset_previous_scope_chart_unlock(parser, keyword, true);
83
84 if(parser->user.cleanup_slots && parser->user.st)
85 rrdset_pluginsd_receive_unslot(parser->user.st);
src/plugins.d/pluginsd_parser.c
+9 -3
@@ -1,6 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "pluginsd_internals.h"
4 +#include "streaming/replication.h"
5
6 static inline PARSER_RC pluginsd_set(char **words, size_t num_words, PARSER *parser) {
7 int idx = 1;
@@ -327,7 +328,7 @@ static inline PARSER_RC pluginsd_chart(char **words, size_t num_words, PARSER *p
328
329 st = rrdset_create(
330 host, type, id, name, family, context, title, units,
330 - (plugin && *plugin) ? plugin : parser->user.cd->filename,
331 + (plugin && *plugin) ? plugin : string2str(parser->user.cd->filename),
332 module, priority, update_every,
333 chart_type);
334
@@ -783,7 +784,7 @@ static inline PARSER_RC pluginsd_begin_v2(char **words, size_t num_words, PARSER
784 // ------------------------------------------------------------------------
785 // prepare our state
786
786 - pluginsd_lock_rrdset_data_collection(parser);
787 + rrdset_data_collection_lock(parser);
788
789 parser->user.v2.update_every = update_every;
790 parser->user.v2.end_time = end_time;
@@ -1034,7 +1035,7 @@ static inline PARSER_RC pluginsd_end_v2(char **words __maybe_unused, size_t num_
1035 // ------------------------------------------------------------------------
1036 // unblock data collection
1037
1037 - pluginsd_unlock_previous_scope_chart(parser, PLUGINSD_KEYWORD_END_V2, false);
1038 + rrdset_previous_scope_chart_unlock(parser, PLUGINSD_KEYWORD_END_V2, false);
1039 rrdcontext_collected_rrdset(st);
1040 store_metric_collection_completed();
1041
@@ -1222,6 +1223,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, int fd_input,
1223 CLEANUP_FUNCTION_REGISTER(pluginsd_process_thread_cleanup) cleanup_parser = parser;
1224 buffered_reader_init(&parser->reader);
1225 CLEAN_BUFFER *buffer = buffer_create(sizeof(parser->reader.read_buffer) + 2, NULL);
1226 + bool send_quit = true;
1227 while(likely(service_running(SERVICE_COLLECTORS))) {
1228
1229 if(unlikely(!buffered_reader_next_line(&parser->reader, buffer))) {
@@ -1231,6 +1233,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, int fd_input,
1233
1234 if(unlikely(ret != BUFFERED_READER_READ_OK)) {
1235 nd_log(NDLS_COLLECTORS, NDLP_INFO, "Buffered reader not OK");
1236 + send_quit = false;
1237 break;
1238 }
1239
@@ -1244,6 +1247,9 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, int fd_input,
1247 buffer->buffer[0] = '\0';
1248 }
1249
1250 + if(send_quit)
1251 + send_to_plugin(PLUGINSD_CALL_QUIT, parser, STREAM_TRAFFIC_TYPE_METADATA);
1252 +
1253 cd->unsafe.enabled = parser->user.enabled;
1254 count = parser->user.data_collections_count;
1255
src/plugins.d/pluginsd_parser.h
+4
@@ -94,6 +94,8 @@ typedef struct parser_user_object {
94 } PARSER_USER_OBJECT;
95
96 typedef void (*parser_deferred_action_t)(struct parser *parser, void *action_data);
97 +struct parser;
98 +typedef ssize_t (*send_to_plugin_callback_t)(const char *txt, void *data, STREAM_TRAFFIC_TYPE type);
99
100 struct parser {
101 uint8_t version; // Parser version
@@ -102,6 +104,8 @@ struct parser {
104 int fd_input;
105 int fd_output;
106 ND_SOCK *sock;
107 + send_to_plugin_callback_t send_to_plugin_cb;
108 + void *send_to_plugin_data;
109
110 #ifdef ENABLE_H2O
111 void *h2o_ctx; // if set we use h2o_stream functions to send data
src/plugins.d/pluginsd_replication.c
+1
@@ -2,6 +2,7 @@
2
3 #include "pluginsd_replication.h"
4 #include "streaming/stream-receiver-internals.h"
5 +#include "streaming/replication.h"
6
7 PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, PARSER *parser) {
8 int idx = 1;
src/streaming/protocol/command-nodeid.c
+2 -2
@@ -34,7 +34,7 @@ void stream_receiver_send_node_and_claim_id_to_child(RRDHOST *host) {
34 PLUGINSD_KEYWORD_NODE_ID " '%s' '%s' '%s'\n",
35 claim_id.str, node_id_str, cloud_config_url_get());
36
37 - send_to_plugin(buf, __atomic_load_n(&host->receiver->thread.parser, __ATOMIC_RELAXED));
37 + send_to_plugin(buf, __atomic_load_n(&host->receiver->thread.parser, __ATOMIC_RELAXED), STREAM_TRAFFIC_TYPE_METADATA);
38 }
39 rrdhost_receiver_unlock(host);
40 }
@@ -74,7 +74,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
74
75 if(!UUIDiszero(s->host->node_id) && !UUIDeq(s->host->node_id, node_id)) {
76 if(claimed) {
77 - nd_log(NDLS_DAEMON, NDLP_ERR,
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.",
79 rrdhost_hostname(s->host), s->connected_to,
80 node_id_str ? node_id_str : "(unset)");
src/streaming/replication.c
+13 -20
@@ -5,7 +5,6 @@
5 #include "replication.h"
6 #include "Judy.h"
7
8 -#define STREAMING_START_MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED 50ULL
8 #define MAX_REPLICATION_MESSAGE_PERCENT_SENDER_BUFFER 25ULL
9 #define MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED 50ULL
10 #define MIN_SENDER_BUFFER_PERCENTAGE_ALLOWED 10ULL
@@ -37,7 +36,7 @@
36 #define REQUESTS_AHEAD_PER_THREAD 1 // 1 = enable synchronous queries
37
38 static struct replication_query_statistics replication_queries = {
40 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
39 + .spinlock = SPINLOCK_INITIALIZER,
40 .queries_started = 0,
41 .queries_finished = 0,
42 .points_read = 0,
@@ -770,7 +769,7 @@ static void replicate_log_request(struct replication_request_details *r, const c
769 internal_error(true,
770 #else
771 nd_log_limit_static_global_var(erl, 1, 0);
773 - nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
772 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
773 #endif
774 "REPLAY ERROR: 'host:%s/chart:%s' child sent: "
775 "db from %ld to %ld%s, wall clock time %ld, "
@@ -837,7 +836,7 @@ static bool send_replay_chart_cmd(struct replication_request_details *r, const c
836 rrdset_id(st), r->wanted.start_streaming ? "true" : "false",
837 (unsigned long long)r->wanted.after, (unsigned long long)r->wanted.before);
838
840 - ssize_t ret = r->caller.callback(buffer, r->caller.parser);
839 + ssize_t ret = r->caller.callback(buffer, r->caller.parser, STREAM_TRAFFIC_TYPE_REPLICATION);
840 if (ret < 0) {
841 netdata_log_error("REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %zd)",
842 rrdhost_hostname(r->host), rrdset_id(r->st), ret);
@@ -962,7 +961,7 @@ bool replicate_chart_request(send_command callback, struct parser *parser, RRDHO
961 r.wanted.after = 0;
962 r.wanted.before = 0;
963 r.wanted.start_streaming = true;
965 - return send_replay_chart_cmd(&r, "empty replication request, wanted after computed bigger than wanted before", true);
964 + return send_replay_chart_cmd(&r, "empty replication request, wanted 'after' computed bigger than wanted 'before'", true);
965 }
966
967 // the child should start streaming immediately if the wanted duration is small, or we reached the last entry of the child
@@ -987,7 +986,7 @@ struct replication_request {
986 time_t after; // the start time of the query (maybe zero) key for sorting (JudyL)
987 time_t before; // the end time of the query (maybe zero)
988
990 - usec_t sender_last_flush_ut; // the timestamp of the sender, at the time we indexed this request
989 + usec_t sender_circular_buffer_since_ut; // the timestamp of the sender, at the time we indexed this request
990 Word_t unique_id; // auto-increment, later requests have bigger
991
992 bool start_streaming; // true, when the parent wants to send the rest of the data (before is overwritten) and enable normal streaming
@@ -1051,7 +1050,7 @@ static struct replication_thread {
1050
1051 } replication_globals = {
1052 .aral_rse = NULL,
1054 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
1053 + .spinlock = SPINLOCK_INITIALIZER,
1054 .unsafe = {
1055 .pending = 0,
1056
@@ -1434,7 +1433,7 @@ static void replication_request_delete_callback(const DICTIONARY_ITEM *item __ma
1433 }
1434
1435 static bool sender_is_still_connected_for_this_request(struct replication_request *rq) {
1437 - return rq->sender_last_flush_ut == stream_sender_get_flush_time(rq->sender);
1436 + return rq->sender_circular_buffer_since_ut == stream_circular_buffer_get_since_ut(rq->sender->scb);
1437 }
1438
1439 static bool replication_execute_request(struct replication_request *rq, bool workers) {
@@ -1467,7 +1466,8 @@ static bool replication_execute_request(struct replication_request *rq, bool wor
1466 // send the replication data
1467 rq->q->rq = rq;
1468 replication_response_execute_and_finalize(
1470 - rq->q, (size_t)((unsigned long long)rq->sender->host->sender->sbuf.cb->max_size * MAX_REPLICATION_MESSAGE_PERCENT_SENDER_BUFFER / 100ULL), workers);
1469 + rq->q,
1470 + (size_t)((unsigned long long)stream_circular_buffer_get_max_size(rq->sender->scb) * MAX_REPLICATION_MESSAGE_PERCENT_SENDER_BUFFER / 100ULL), workers);
1471
1472 rq->q = NULL;
1473
@@ -1496,7 +1496,7 @@ void replication_add_request(struct sender_state *sender, const char *chart_id,
1496 .after = after,
1497 .before = before,
1498 .start_streaming = start_streaming,
1499 - .sender_last_flush_ut = stream_sender_get_flush_time(sender),
1499 + .sender_circular_buffer_since_ut = stream_circular_buffer_get_since_ut(sender->scb),
1500 .indexed_in_judy = false,
1501 .not_indexed_buffer_full = false,
1502 .not_indexed_preprocessing = false,
@@ -1505,10 +1505,6 @@ void replication_add_request(struct sender_state *sender, const char *chart_id,
1505 if(!sender->replication.oldest_request_after_t || rq.after < sender->replication.oldest_request_after_t)
1506 sender->replication.oldest_request_after_t = rq.after;
1507
1508 -// if(start_streaming && rrdpush_sender_get_buffer_used_percent(sender) <= STREAMING_START_MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED)
1509 -// replication_execute_request(&rq, false);
1510 -//
1511 -// else
1508 dictionary_set(sender->replication.requests, chart_id, &rq, sizeof(struct replication_request));
1509 }
1510
@@ -1535,8 +1531,7 @@ void replication_cleanup_sender(struct sender_state *sender) {
1531 }
1532
1533 void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) {
1538 - size_t available = cbuffer_available_size_unsafe(s->host->sender->sbuf.cb);
1539 - size_t percentage = (s->sbuf.cb->max_size - available) * 100 / s->sbuf.cb->max_size;
1534 + size_t percentage = stream_sender_get_buffer_used_percent(s->scb);
1535
1536 if(unlikely(percentage > MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED && !stream_sender_replication_buffer_full_get(s))) {
1537 stream_sender_replication_buffer_full_set(s, true);
@@ -1568,8 +1563,6 @@ void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) {
1563 // replication_set_next_point_in_time(0, 0);
1564 replication_recursive_unlock();
1565 }
1571 -
1572 - stream_sender_set_buffer_used_percent(s, percentage);
1566 }
1567
1568 // ----------------------------------------------------------------------------
@@ -1775,7 +1768,7 @@ static int replication_pipeline_execute_next(void) {
1768 if(rq->found) {
1769 internal_fatal(rq->executed, "REPLAY FATAL: query has already been executed!");
1770
1778 - if (rq->sender_last_flush_ut != stream_sender_get_flush_time(rq->sender)) {
1771 + if (rq->sender_circular_buffer_since_ut != stream_circular_buffer_get_since_ut(rq->sender->scb)) {
1772 // the sender has reconnected since this request was queued,
1773 // we can safely throw it away, since the parent will resend it
1774 replication_response_cancel_and_finalize(rq->q);
@@ -1887,7 +1880,7 @@ void *replication_thread_main(void *ptr) {
1880
1881 int nodes = (int)dictionary_entries(rrdhost_root_index);
1882 int cpus = (int)get_netdata_cpus();
1890 - int threads = MIN(cpus * 2 / 3, nodes / 5);
1883 + int threads = MIN(cpus * 1 / 3, nodes / 10);
1884 if (threads < 1) threads = 1;
1885 else if (threads > MAX_REPLICATION_THREADS) threads = MAX_REPLICATION_THREADS;
1886
src/streaming/replication.h
+2 -1
@@ -4,6 +4,7 @@
4 #define REPLICATION_H
5
6 #include "daemon/common.h"
7 +#include "stream-circular-buffer.h"
8
9 struct parser;
10
@@ -19,7 +20,7 @@ struct replication_query_statistics replication_get_query_statistics(void);
20
21 bool replicate_chart_response(RRDHOST *rh, RRDSET *rs, bool start_streaming, time_t after, time_t before);
22
22 -typedef ssize_t (*send_command)(const char *txt, struct parser *parser);
23 +typedef ssize_t (*send_command)(const char *txt, struct parser *parser, STREAM_TRAFFIC_TYPE type);
24
25 bool replicate_chart_request(send_command callback, struct parser *parser,
26 RRDHOST *rh, RRDSET *rs,
src/streaming/rrdhost-status.c
+8 -4
@@ -205,10 +205,14 @@ void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) {
205 s->stream.peers = nd_sock_socket_peers(&host->sender->sock);
206 s->stream.ssl = nd_sock_is_ssl(&host->sender->sock);
207
208 - memcpy(s->stream.sent_bytes_on_this_connection_per_type,
209 - host->sender->thread.bytes_sent_by_type,
210 - MIN(sizeof(s->stream.sent_bytes_on_this_connection_per_type),
211 - sizeof(host->sender->thread.bytes_sent_by_type)));
208 + {
209 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(host->sender->scb);
210 +
211 + memcpy(
212 + s->stream.sent_bytes_on_this_connection_per_type,
213 + stats->bytes_sent_by_type,
214 + MIN(sizeof(s->stream.sent_bytes_on_this_connection_per_type), sizeof(stats->bytes_sent_by_type)));
215 + }
216
217 if (rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_SENDER_CONNECTED)) {
218 s->stream.hops = host->sender->hops;
src/streaming/stream-circular-buffer.c new
+126
@@ -0,0 +1,126 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "stream.h"
4 +#include "stream-sender-internals.h"
5 +
6 +#define STREAM_CIRCULAR_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3
7 +
8 +struct stream_circular_buffer {
9 + struct circular_buffer *cb;
10 + STREAM_CIRCULAR_BUFFER_STATS stats;
11 + usec_t last_reset_ut;
12 +
13 + struct {
14 + // the current max size of the buffer
15 + size_t max_size;
16 +
17 + // the current utilization of the buffer
18 + size_t buffer_ratio;
19 +
20 + // the last time we flushed the buffer
21 + // by monitoring this we can know if the system was reconnected
22 + usec_t since_ut;
23 + } atomic;
24 +};
25 +
26 +static inline void stream_circular_buffer_stats_update_unsafe(STREAM_CIRCULAR_BUFFER *scb) {
27 + scb->stats.bytes_size = scb->cb->size;
28 + scb->stats.bytes_max_size = scb->cb->max_size;
29 + scb->stats.bytes_outstanding = cbuffer_next_unsafe(scb->cb, NULL);
30 + scb->stats.bytes_available = cbuffer_available_size_unsafe(scb->cb);
31 + scb->stats.buffer_ratio = (double)(scb->cb->max_size - scb->stats.bytes_available) * 100.0 / (double)scb->cb->max_size;
32 +
33 + __atomic_store_n(&((scb)->atomic.buffer_ratio), (size_t)round(scb->stats.buffer_ratio), __ATOMIC_RELAXED);
34 +}
35 +
36 +STREAM_CIRCULAR_BUFFER *stream_circular_buffer_create(void) {
37 + STREAM_CIRCULAR_BUFFER *scb = callocz(1, sizeof(*scb));
38 + scb->cb = cbuffer_new(CBUFFER_INITIAL_SIZE, CBUFFER_INITIAL_MAX_SIZE, &netdata_buffers_statistics.cbuffers_streaming);
39 + stream_circular_buffer_stats_update_unsafe(scb);
40 + return scb;
41 +}
42 +
43 +// 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;
48 + scb->stats.bytes_max_size = scb->cb->max_size;
49 + __atomic_store_n(&scb->atomic.max_size, scb->cb->max_size, __ATOMIC_RELAXED);
50 + stream_circular_buffer_stats_update_unsafe(scb);
51 + return true;
52 + }
53 +
54 + return false;
55 +}
56 +
57 +void stream_circular_buffer_flush_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t buffer_max_size) {
58 + __atomic_store_n(&((scb)->atomic.since_ut), now_monotonic_usec(), __ATOMIC_RELAXED);
59 +
60 + // flush the output buffer from any data it may have
61 + cbuffer_flush(scb->cb);
62 + memset(&scb->stats, 0, sizeof(scb->stats));
63 + stream_circular_buffer_set_max_size_unsafe(scb, buffer_max_size, true);
64 + stream_circular_buffer_recreate_timed_unsafe(scb, now_monotonic_usec(), true);
65 +}
66 +
67 +inline size_t stream_sender_get_buffer_used_percent(STREAM_CIRCULAR_BUFFER *scb) {
68 + return __atomic_load_n(&((scb)->atomic.buffer_ratio), __ATOMIC_RELAXED);
69 +}
70 +
71 +size_t stream_circular_buffer_get_max_size(STREAM_CIRCULAR_BUFFER *scb) {
72 + return __atomic_load_n(&scb->atomic.max_size, __ATOMIC_RELAXED);
73 +}
74 +
75 +void stream_circular_buffer_recreate_timed_unsafe(STREAM_CIRCULAR_BUFFER *scb, usec_t now_ut, bool force) {
76 + if(!force && (scb->stats.bytes_outstanding || now_ut - scb->last_reset_ut < 300 * USEC_PER_SEC))
77 + return;
78 +
79 + scb->last_reset_ut = now_ut;
80 +
81 + scb->stats.recreates++; // we increase even if we don't do it, to have sender_start() recreate its buffers
82 +
83 + if(scb->cb && scb->cb->size > CBUFFER_INITIAL_SIZE) {
84 + cbuffer_free(scb->cb);
85 + scb->cb = cbuffer_new(CBUFFER_INITIAL_SIZE, stream_send.buffer_max_size, &netdata_buffers_statistics.cbuffers_streaming);
86 + }
87 +}
88 +
89 +inline usec_t stream_circular_buffer_get_since_ut(STREAM_CIRCULAR_BUFFER *scb) {
90 + return __atomic_load_n(&((scb)->atomic.since_ut), __ATOMIC_RELAXED);
91 +}
92 +
93 +void stream_circular_buffer_destroy(STREAM_CIRCULAR_BUFFER *scb) {
94 + cbuffer_free(scb->cb);
95 + freez(scb);
96 +}
97 +
98 +// 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) {
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;
108 +}
109 +
110 +// return the first available chunk at the beginning of the buffer
111 +size_t stream_circular_buffer_get_unsafe(STREAM_CIRCULAR_BUFFER *scb, char **chunk) {
112 + return cbuffer_next_unsafe(scb->cb, chunk);
113 +}
114 +
115 +// removes data from the beginning of the circular buffer
116 +void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes) {
117 + scb->stats.sends++;
118 + scb->stats.bytes_sent += bytes;
119 + cbuffer_remove_unsafe(scb->cb, bytes);
120 + stream_circular_buffer_stats_update_unsafe(scb);
121 +}
122 +
123 +// returns a copy of the current circular buffer statistics
124 +STREAM_CIRCULAR_BUFFER_STATS *stream_circular_buffer_stats_unsafe(STREAM_CIRCULAR_BUFFER *scb) {
125 + return &scb->stats;
126 +}
src/streaming/stream-circular-buffer.h new
+83
@@ -0,0 +1,83 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STREAM_CIRCULAR_BUFFER_H
4 +#define NETDATA_STREAM_CIRCULAR_BUFFER_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +#include "stream-traffic-types.h"
8 +
9 +#define CBUFFER_INITIAL_SIZE (16 * 1024)
10 +#define CBUFFER_INITIAL_MAX_SIZE (10 * 1024 * 1024)
11 +#define THREAD_BUFFER_INITIAL_SIZE (8192)
12 +
13 +typedef struct stream_circular_buffer_stats {
14 + size_t adds;
15 + size_t sends;
16 + size_t recreates;
17 +
18 + size_t bytes_added;
19 + size_t bytes_uncompressed;
20 + size_t bytes_sent;
21 +
22 + uint32_t bytes_size;
23 + uint32_t bytes_max_size;
24 + uint32_t bytes_outstanding;
25 + uint32_t bytes_available;
26 +
27 + double buffer_ratio;
28 +
29 + size_t bytes_sent_by_type[STREAM_TRAFFIC_TYPE_MAX];
30 +} STREAM_CIRCULAR_BUFFER_STATS;
31 +
32 +struct stream_circular_buffer;
33 +typedef struct stream_circular_buffer STREAM_CIRCULAR_BUFFER;
34 +
35 +// --------------------------------------------------------------------------------------------------------------------
36 +// management
37 +
38 +STREAM_CIRCULAR_BUFFER *stream_circular_buffer_create(void);
39 +void stream_circular_buffer_destroy(STREAM_CIRCULAR_BUFFER *scb);
40 +
41 +// flushes all data in the buffer
42 +void stream_circular_buffer_flush_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t buffer_max_size);
43 +
44 +// recreates the buffer, but it does so every 5 minutes and only if the buffer has no data in it
45 +// it does not alter the since_ut time of the buffer, so this is assumed to be the same session
46 +// use this after deleting data from the buffer, to minimize the memory footprint of the buffer
47 +void stream_circular_buffer_recreate_timed_unsafe(STREAM_CIRCULAR_BUFFER *scb, usec_t now_ut, bool force);
48 +
49 +// returns true if it increased the buffer size
50 +// 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);
52 +
53 +// returns a pointer to the current circular buffer statistics
54 +// copy it if you plan to use it without a lock
55 +STREAM_CIRCULAR_BUFFER_STATS *stream_circular_buffer_stats_unsafe(STREAM_CIRCULAR_BUFFER *scb);
56 +
57 +// --------------------------------------------------------------------------------------------------------------------
58 +// atomic operations - no lock needed
59 +
60 +// returns the max size of the buffer in bytes
61 +size_t stream_circular_buffer_get_max_size(STREAM_CIRCULAR_BUFFER *scb);
62 +
63 +// returns the current buffer used ratio
64 +size_t stream_sender_get_buffer_used_percent(STREAM_CIRCULAR_BUFFER *scb);
65 +
66 +// return the monotonic timestamp of the last time the buffer was created
67 +usec_t stream_circular_buffer_get_since_ut(STREAM_CIRCULAR_BUFFER *scb);
68 +
69 +// --------------------------------------------------------------------------------------------------------------------
70 +// data operations (add, get, remove data from/to the buffer)
71 +
72 +// adds data to the end of the circular buffer, returns false when it can't (buffer is full)
73 +// 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);
75 +
76 +// returns a pointer to the beginning of the buffer, and its size in bytes
77 +size_t stream_circular_buffer_get_unsafe(STREAM_CIRCULAR_BUFFER *scb, char **chunk);
78 +
79 +// removes data from the beginning of circular buffer
80 +// it updates the statistics
81 +void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes);
82 +
83 +#endif //NETDATA_STREAM_CIRCULAR_BUFFER_H
src/streaming/stream-connector.c
+4 -5
@@ -232,7 +232,7 @@ static int stream_connect_upgrade_prelude(RRDHOST *host __maybe_unused, struct s
232 error_report("Missing \"connection\" header in reply");
233 goto err_cleanup;
234 }
235 - if (strncmp(hdr, CONN_UPGRADE_VAL, strlen(CONN_UPGRADE_VAL))) {
235 + if (strncmp(hdr, CONN_UPGRADE_VAL, strlen(CONN_UPGRADE_VAL)) != 0) {
236 error_report("Expected \"connection: " CONN_UPGRADE_VAL "\"");
237 goto err_cleanup;
238 }
@@ -242,7 +242,7 @@ static int stream_connect_upgrade_prelude(RRDHOST *host __maybe_unused, struct s
242 error_report("Missing \"upgrade\" header in reply");
243 goto err_cleanup;
244 }
245 - if (strncmp(hdr, NETDATA_STREAM_PROTO_NAME, strlen(NETDATA_STREAM_PROTO_NAME))) {
245 + if (strncmp(hdr, NETDATA_STREAM_PROTO_NAME, strlen(NETDATA_STREAM_PROTO_NAME)) != 0) {
246 error_report("Expected \"upgrade: " NETDATA_STREAM_PROTO_NAME "\"");
247 goto err_cleanup;
248 }
@@ -566,7 +566,7 @@ struct connector *stream_connector_get(struct sender_state *s) {
566 if(s->connector.id < 0 || s->connector.id >= MAX_CONNECTORS) {
567 // assign this to the dispatcher with fewer nodes
568
569 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
569 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
570 spinlock_lock(&spinlock);
571 int min_slot = 0;
572 size_t min_nodes = __atomic_load_n(&connector_globals.connectors[0].nodes, __ATOMIC_RELAXED);
@@ -624,7 +624,6 @@ void stream_connector_add(struct sender_state *s) {
624 stream_sender_unlock(s);
625
626 nd_sock_close(&s->sock);
627 - s->sbuf.cb->max_size = stream_send.buffer_max_size;
627 s->parent_using_h2o = stream_send.parents.h2o;
628
629 // do not call this with any locks held
@@ -721,7 +720,7 @@ static void *stream_connector_thread(void *ptr) {
720 }
721
722 bool stream_connector_init(struct sender_state *s) {
724 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
723 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
724 if(!s) return false;
725
726 spinlock_lock(&spinlock);
src/streaming/stream-handshake.c
+3 -3
@@ -32,15 +32,15 @@ static struct {
32 {STREAM_HANDSHAKE_DISCONNECT_RECEIVER_LEFT, "DISCONNECTED RECEIVER LEFT"},
33 {STREAM_HANDSHAKE_DISCONNECT_ORPHAN_HOST, "DISCONNECTED ORPHAN HOST"},
34 {STREAM_HANDSHAKE_NON_STREAMABLE_HOST, "NON STREAMABLE HOST"},
35 - {STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_RECEIVER_READ_BUFFER, "DISCONNECTED NOT SUFFICIENT RCV READ BUFFER"},
35 + {STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_READ_BUFFER, "DISCONNECTED NOT SUFFICIENT READ BUFFER"},
36 {STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SENDER_COMPRESSION_FAILED, "DISCONNECTED SND COMPRESSION FAILED"},
37 - {STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SENDER_SEND_BUFFER, "DISCONNECTED NOT SUFFICIENT SEND BUFFER"},
37 + {STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SEND_BUFFER, "DISCONNECTED NOT SUFFICIENT SEND BUFFER"},
38 {STREAM_HANDSHAKE_DISCONNECT_SOCKET_EOF, "DISCONNECTED SOCKET EOF"},
39 {STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, "DISCONNECTED SOCKET READ FAILED"},
40 {STREAM_HANDSHAKE_DISCONNECT_SOCKET_TIMEOUT, "DISCONNECTED SOCKET TIMEOUT"},
41 {STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, "DISCONNECT SOCKET ERROR"},
42 {STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED, "DISCONNECTED SOCKET WRITE FAILED"},
43 - {STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_PARENT, "DISCONNECTED SOCKET CLOSED BY PARENT"},
43 + {STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, "DISCONNECTED SOCKET CLOSED BY REMOTE END"},
44 {STREAM_HANDSHAKE_ERROR_HTTP_UPGRADE, "HTTP UPGRADE ERROR"},
45 {STREAM_HANDSHAKE_NO_HOST_IN_DESTINATION, "NO HOST IN DESTINATION - CONFIG ERROR"},
46 {STREAM_HANDSHAKE_CONNECT_TIMEOUT, "CONNECT TIMEOUT"},
src/streaming/stream-handshake.h
+6 -3
@@ -61,20 +61,23 @@ typedef enum {
61 STREAM_HANDSHAKE_DISCONNECT_SHUTDOWN = -15,
62 STREAM_HANDSHAKE_DISCONNECT_NETDATA_EXIT = -16,
63 STREAM_HANDSHAKE_DISCONNECT_PARSER_EXIT = -17,
64 +
65 STREAM_HANDSHAKE_DISCONNECT_UNKNOWN_SOCKET_READ_ERROR = -18,
66 STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED = -19,
67 STREAM_HANDSHAKE_DISCONNECT_RECEIVER_LEFT = -20,
68 STREAM_HANDSHAKE_DISCONNECT_ORPHAN_HOST = -21,
69 STREAM_HANDSHAKE_NON_STREAMABLE_HOST = -22,
69 - STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_RECEIVER_READ_BUFFER = -23,
70 +
71 + STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_READ_BUFFER = -23,
72 + STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SEND_BUFFER = -25,
73 +
74 STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SENDER_COMPRESSION_FAILED = -24,
71 - STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SENDER_SEND_BUFFER = -25,
75 STREAM_HANDSHAKE_DISCONNECT_SOCKET_EOF = -26,
76 STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED = -27,
77 STREAM_HANDSHAKE_DISCONNECT_SOCKET_TIMEOUT = -28,
78 STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR = -29,
79 STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED = -30,
77 - STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_PARENT = -31,
80 + STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END = -31,
81 STREAM_HANDSHAKE_ERROR_HTTP_UPGRADE = -32,
82 STREAM_HANDSHAKE_NO_HOST_IN_DESTINATION = -33,
83 STREAM_HANDSHAKE_CONNECT_TIMEOUT = -34,
src/streaming/stream-parents.c
+2 -2
@@ -54,7 +54,7 @@ struct blocked_parent {
54
55 DEFINE_JUDYL_TYPED(BLOCKED_PARENTS, struct blocked_parent *);
56 static BLOCKED_PARENTS_JudyLSet blocked_parents_set = { 0 };
57 -static RW_SPINLOCK blocked_parents_spinlock = NETDATA_RW_SPINLOCK_INITIALIZER;
57 +static RW_SPINLOCK blocked_parents_spinlock = RW_SPINLOCK_INITIALIZER;
58
59 static void block_parent_for_all_nodes(STREAM_PARENT *d, time_t duration_s) {
60 rw_spinlock_write_lock(&blocked_parents_spinlock);
@@ -195,7 +195,7 @@ void rrdhost_stream_parents_to_json(BUFFER *wb, RRDHOST_STATUS *s) {
195 }
196
197 void rrdhost_stream_parent_ssl_init(struct sender_state *s) {
198 - static SPINLOCK sp = NETDATA_SPINLOCK_INITIALIZER;
198 + static SPINLOCK sp = SPINLOCK_INITIALIZER;
199 spinlock_lock(&sp);
200
201 if(netdata_ssl_streaming_sender_ctx || !s->host) {
src/streaming/stream-path.c
+1 -1
@@ -241,7 +241,7 @@ void stream_path_send_to_child(RRDHOST *host) {
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));
244 - send_to_plugin(buffer_tostring(wb), __atomic_load_n(&host->receiver->thread.parser, __ATOMIC_RELAXED));
244 + send_to_plugin(buffer_tostring(wb), __atomic_load_n(&host->receiver->thread.parser, __ATOMIC_RELAXED), STREAM_TRAFFIC_TYPE_METADATA);
245 }
246 rrdhost_receiver_unlock(host);
247 }
src/streaming/stream-receiver-connection.c
+7 -1
@@ -57,6 +57,10 @@ void stream_receiver_free(struct receiver_state *rpt) {
57 freez(rpt->program_name);
58 freez(rpt->program_version);
59
60 + freez(rpt->thread.compressed.buf);
61 + rpt->thread.compressed.buf = NULL;
62 + rpt->thread.compressed.size = 0;
63 +
64 string_freez(rpt->config.send.api_key);
65 string_freez(rpt->config.send.parents);
66 string_freez(rpt->config.send.charts_matching);
@@ -261,6 +265,8 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
265 return stream_receiver_response_too_busy_now(w);
266
267 struct receiver_state *rpt = callocz(1, sizeof(*rpt));
268 + rpt->thread.compressed.size = COMPRESSION_MAX_CHUNK;
269 + rpt->thread.compressed.buf = mallocz(rpt->thread.compressed.size);
270 rpt->connected_since_s = now_realtime_sec();
271 rpt->last_msg_t = now_monotonic_sec();
272 rpt->hops = 1;
@@ -531,7 +537,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
537 }
538
539 if(unlikely(web_client_streaming_rate_t > 0)) {
534 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
540 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
541 static time_t last_stream_accepted_t = 0;
542
543 time_t now = now_realtime_sec();
src/streaming/stream-receiver-internals.h
+12 -1
@@ -47,10 +47,18 @@ struct receiver_state {
47 bool enabled;
48 size_t start;
49 size_t used;
50 - char buf[COMPRESSION_MAX_CHUNK * 2];
50 + size_t size;
51 + char *buf;
52 struct decompressor_state decompressor;
53 } compressed;
54
55 + struct {
56 + SPINLOCK spinlock;
57 + struct stream_opcode msg;
58 + uint32_t msg_slot;
59 + STREAM_CIRCULAR_BUFFER *scb;
60 + } send_to_child;
61 +
62 struct pollfd_meta meta;
63 } thread;
64
@@ -80,5 +88,8 @@ void stream_receiver_log_status(struct receiver_state *rpt, const char *msg, con
88 void stream_receiver_free(struct receiver_state *rpt);
89 bool stream_receiver_signal_to_stop_and_wait(RRDHOST *host, STREAM_HANDSHAKE reason);
90
91 +ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE type);
92 +void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcode msg);
93 +void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state *rpt, struct stream_opcode *msg);
94
95 #endif //NETDATA_STREAM_RECEIVER_INTERNALS_H
src/streaming/stream-receiver.c
+388 -188
@@ -5,6 +5,8 @@
5 #include "stream-receiver-internals.h"
6 #include "web/server/h2o/http_server.h"
7
8 +static void stream_receiver_remove(struct stream_thread *sth, struct receiver_state *rpt, const char *why);
9 +
10 // When a child disconnects this is the maximum we will wait
11 // before we update the cloud that the child is offline
12 #define MAX_CHILD_DISC_DELAY (30000)
@@ -28,59 +30,68 @@ static void streaming_receiver_disconnected(void) {
30
31 // --------------------------------------------------------------------------------------------------------------------
32
31 -static inline ssize_t read_stream(struct receiver_state *r, char* buffer, size_t size) {
33 +static bool stream_receiver_log_capabilities(BUFFER *wb, void *ptr) {
34 + struct receiver_state *rpt = ptr;
35 + if(!rpt)
36 + return false;
37 +
38 + stream_capabilities_to_string(wb, rpt->capabilities);
39 + return true;
40 +}
41 +
42 +static bool stream_receiver_log_transport(BUFFER *wb, void *ptr) {
43 + struct receiver_state *rpt = ptr;
44 + if(!rpt)
45 + return false;
46 +
47 + buffer_strcat(wb, nd_sock_is_ssl(&rpt->sock) ? "https" : "http");
48 + return true;
49 +}
50 +
51 +// --------------------------------------------------------------------------------------------------------------------
52 +
53 +static inline ssize_t write_stream(struct receiver_state *r, char* buffer, size_t size) {
54 if(unlikely(!size)) {
55 internal_error(true, "%s() asked to read zero bytes", __FUNCTION__);
56 + errno_clear();
57 return -2;
58 }
59
60 #ifdef ENABLE_H2O
61 if (is_h2o_rrdpush(r)) {
39 - if(nd_thread_signaled_to_cancel())
62 + if(nd_thread_signaled_to_cancel()) {
63 + errno_clear();
64 return -3;
65 + }
66
42 - return (ssize_t)h2o_stream_read(r->h2o_ctx, buffer, size);
67 + return (ssize_t)h2o_stream_write(r->h2o_ctx, buffer, size);
68 }
69 #endif
70
46 - ssize_t bytes_read = nd_sock_read(&r->sock, buffer, size, 0);
47 - if(bytes_read <= 0) {
48 - if (bytes_read == 0)
49 - netdata_log_error("STREAM: %s(): EOF while reading data from socket!", __FUNCTION__);
50 - else {
51 - netdata_log_error("STREAM: %s() failed to read from socket!", __FUNCTION__);
52 - bytes_read = -1;
53 - }
54 - }
55 -
56 - return bytes_read;
71 + ssize_t bytes_written = nd_sock_send_nowait(&r->sock, buffer, size);
72 + return bytes_written;
73 }
74
59 -static inline STREAM_HANDSHAKE read_stream_error_to_reason(ssize_t code) {
60 - if(code > 0)
61 - return 0;
62 -
63 - switch(code) {
64 - case 0:
65 - // EOF
66 - return STREAM_HANDSHAKE_DISCONNECT_SOCKET_EOF;
67 -
68 - case -1:
69 - // failed to read
70 - return STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED;
71 -
72 - case -2:
73 - // asked to read zero bytes
74 - return STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_RECEIVER_READ_BUFFER;
75 +static inline ssize_t read_stream(struct receiver_state *r, char* buffer, size_t size) {
76 + if(unlikely(!size)) {
77 + internal_error(true, "%s() asked to read zero bytes", __FUNCTION__);
78 + errno_clear();
79 + return -2;
80 + }
81
76 - case -3:
77 - // the thread is cancelled
78 - return STREAM_HANDSHAKE_DISCONNECT_SHUTDOWN;
82 +#ifdef ENABLE_H2O
83 + if (is_h2o_rrdpush(r)) {
84 + if(nd_thread_signaled_to_cancel()) {
85 + errno_clear();
86 + return -3;
87 + }
88
80 - default:
81 - // anything else
82 - return STREAM_HANDSHAKE_DISCONNECT_UNKNOWN_SOCKET_READ_ERROR;
89 + return (ssize_t)h2o_stream_read(r->h2o_ctx, buffer, size);
90 }
91 +#endif
92 +
93 + ssize_t bytes_read = nd_sock_revc_nowait(&r->sock, buffer, size);
94 + return bytes_read;
95 }
96
97 // --------------------------------------------------------------------------------------------------------------------
@@ -200,7 +211,7 @@ static inline ssize_t receiver_read_compressed(struct receiver_state *r) {
211 "%s: read_buffer does not start with zero #2", __FUNCTION__ );
212
213 ssize_t bytes_read = read_stream(r, r->thread.compressed.buf + r->thread.compressed.used,
203 - sizeof(r->thread.compressed.buf) - r->thread.compressed.used);
214 + r->thread.compressed.size - r->thread.compressed.used);
215
216 if(bytes_read > 0) {
217 r->thread.compressed.used += bytes_read;
@@ -228,11 +239,73 @@ static inline bool receiver_should_stop(struct receiver_state *rpt) {
239
240 // --------------------------------------------------------------------------------------------------------------------
241
242 +void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state *rpt, struct stream_opcode *msg) {
243 + ND_LOG_STACK lgs[] = {
244 + ND_LOG_FIELD_STR(NDF_NIDL_NODE, rpt->host->hostname),
245 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->client_ip),
246 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->client_port),
247 + ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
248 + ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
249 + ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
250 + ND_LOG_FIELD_END(),
251 + };
252 + ND_LOG_STACK_PUSH(lgs);
253 +
254 + if(msg->opcode & STREAM_OPCODE_RECEIVER_BUFFER_OVERFLOW) {
255 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW);
256 + errno_clear();
257 + spinlock_lock(&rpt->thread.send_to_child.spinlock);
258 + // copy the statistics
259 + STREAM_CIRCULAR_BUFFER_STATS stats = *stream_circular_buffer_stats_unsafe(rpt->thread.send_to_child.scb);
260 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
261 + 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). "
263 + "Restarting connection.",
264 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip,
265 + stats.bytes_size, stats.bytes_max_size, stats.bytes_outstanding, stats.bytes_available);
266 +
267 + stream_receiver_remove(sth, rpt, "receiver send buffer overflow");
268 + return;
269 + }
270 +
271 + nd_log(NDLS_DAEMON, NDLP_ERR,
272 + "STREAM RECEIVE[%zu]: invalid msg id %u", sth->id, (unsigned)msg->opcode);
273 +}
274 +
275 +ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE type) {
276 + struct receiver_state *rpt = data;
277 + if(!rpt || rpt->thread.meta.type != POLLFD_TYPE_RECEIVER || !rpt->thread.send_to_child.scb)
278 + return 0;
279 +
280 + spinlock_lock(&rpt->thread.send_to_child.spinlock);
281 + STREAM_CIRCULAR_BUFFER *scb = rpt->thread.send_to_child.scb;
282 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(scb);
283 + bool was_empty = stats->bytes_outstanding == 0;
284 + struct stream_opcode msg = rpt->thread.send_to_child.msg;
285 + msg.opcode = STREAM_OPCODE_NONE;
286 +
287 + size_t size = strlen(txt);
288 + ssize_t rc = (ssize_t)size;
289 + if(!stream_circular_buffer_add_unsafe(scb, txt, size, size, type)) {
290 + msg.opcode = STREAM_OPCODE_RECEIVER_BUFFER_OVERFLOW;
291 + rc = -1;
292 + }
293 + else if(was_empty)
294 + msg.opcode = STREAM_OPCODE_RECEIVER_POLLOUT;
295 +
296 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
297 +
298 + if(msg.opcode != STREAM_OPCODE_NONE)
299 + stream_receiver_send_opcode(rpt, msg);
300 +
301 + return rc;
302 +}
303 +
304 static void streaming_parser_init(struct receiver_state *rpt) {
305 rpt->thread.cd = (struct plugind){
306 .update_every = default_rrd_update_every,
307 .unsafe = {
235 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
308 + .spinlock = SPINLOCK_INITIALIZER,
309 .running = true,
310 .enabled = true,
311 },
@@ -240,10 +313,25 @@ static void streaming_parser_init(struct receiver_state *rpt) {
313 };
314
315 // put the client IP and port into the buffers used by plugins.d
243 - snprintfz(rpt->thread.cd.id, CONFIG_MAX_NAME, "%s:%s", rpt->client_ip, rpt->client_port);
244 - snprintfz(rpt->thread.cd.filename, FILENAME_MAX, "%s:%s", rpt->client_ip, rpt->client_port);
245 - snprintfz(rpt->thread.cd.fullfilename, FILENAME_MAX, "%s:%s", rpt->client_ip, rpt->client_port);
246 - snprintfz(rpt->thread.cd.cmd, PLUGINSD_CMD_MAX, "%s:%s", rpt->client_ip, rpt->client_port);
316 + {
317 + char buf[CONFIG_MAX_NAME];
318 + snprintfz(buf, sizeof(buf), "%s:%s", rpt->client_ip, rpt->client_port);
319 + string_freez(rpt->thread.cd.id);
320 + rpt->thread.cd.id = string_strdupz(buf);
321 + }
322 +
323 + {
324 + char buf[FILENAME_MAX + 1];
325 + snprintfz(buf, sizeof(buf), "%s:%s", rpt->client_ip, rpt->client_port);
326 + string_freez(rpt->thread.cd.filename);
327 + rpt->thread.cd.filename = string_strdupz(buf);
328 +
329 + string_freez(rpt->thread.cd.fullfilename);
330 + rpt->thread.cd.fullfilename = string_strdupz(buf);
331 +
332 + string_freez(rpt->thread.cd.cmd);
333 + rpt->thread.cd.cmd = string_strdupz(buf);
334 + }
335
336 PARSER *parser = NULL;
337 {
@@ -257,6 +345,8 @@ static void streaming_parser_init(struct receiver_state *rpt) {
345 };
346
347 parser = parser_init(&user, -1, -1, PARSER_INPUT_SPLIT, &rpt->sock);
348 + parser->send_to_plugin_data = rpt;
349 + parser->send_to_plugin_cb = send_to_child;
350 }
351
352 #ifdef ENABLE_H2O
@@ -294,26 +384,6 @@ static void streaming_parser_init(struct receiver_state *rpt) {
384
385 // --------------------------------------------------------------------------------------------------------------------
386
297 -static bool stream_receiver_log_capabilities(BUFFER *wb, void *ptr) {
298 - struct receiver_state *rpt = ptr;
299 - if(!rpt)
300 - return false;
301 -
302 - stream_capabilities_to_string(wb, rpt->capabilities);
303 - return true;
304 -}
305 -
306 -static bool stream_receiver_log_transport(BUFFER *wb, void *ptr) {
307 - struct receiver_state *rpt = ptr;
308 - if(!rpt)
309 - return false;
310 -
311 - buffer_strcat(wb, nd_sock_is_ssl(&rpt->sock) ? "https" : "http");
312 - return true;
313 -}
314 -
315 -// --------------------------------------------------------------------------------------------------------------------
316 -
387 void stream_receiver_move_queue_to_running_unsafe(struct stream_thread *sth) {
388 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
389
@@ -337,22 +407,64 @@ void stream_receiver_move_queue_to_running_unsafe(struct stream_thread *sth) {
407 "STREAM RECEIVE[%zu] [%s]: moving host from receiver queue to receiver running...",
408 sth->id, rrdhost_hostname(rpt->host));
409
340 - internal_fatal(RECEIVERS_GET(&sth->rcv.receivers, (Word_t)rpt) != NULL, "Receiver to be added is already in the list of receivers");
341 - RECEIVERS_SET(&sth->rcv.receivers, (Word_t)rpt, rpt);
342 -
343 - streaming_parser_init(rpt);
344 -
410 rpt->host->stream.rcv.status.tid = gettid_cached();
411 rpt->thread.meta.type = POLLFD_TYPE_RECEIVER;
412 rpt->thread.meta.rpt = rpt;
413 +
414 + spinlock_lock(&rpt->thread.send_to_child.spinlock);
415 + 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);
419 + rpt->thread.send_to_child.msg.thread_slot = (int32_t)sth->id;
420 + rpt->thread.send_to_child.msg.session = os_random32();
421 + rpt->thread.send_to_child.msg.meta = &rpt->thread.meta;
422 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
423 +
424 + internal_fatal(META_GET(&sth->run.meta, (Word_t)&rpt->thread.meta) != NULL, "Receiver to be added is already in the list of receivers");
425 + META_SET(&sth->run.meta, (Word_t)&rpt->thread.meta, &rpt->thread.meta);
426 +
427 + if(sock_setnonblock(rpt->sock.fd) < 0)
428 + nd_log(NDLS_DAEMON, NDLP_ERR,
429 + "STREAM RECEIVE '%s' [from [%s]:%s]: cannot set the non-blocking flag from socket %d",
430 + rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
431 +
432 if(!nd_poll_add(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
349 - internal_fatal(true, "Failed to add receiver socket to nd_poll()");
433 + nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to add receiver socket to nd_poll()");
434 +
435 + // keep this last, since it sends commands back to the child
436 + streaming_parser_init(rpt);
437 }
438 }
439
353 -static void stream_receiver_on_disconnect(struct stream_thread *sth __maybe_unused, struct receiver_state *rpt) {
440 +static void stream_receiver_remove(struct stream_thread *sth, struct receiver_state *rpt, const char *why) {
441 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
355 - if(!rpt) return;
442 +
443 + nd_log(NDLS_DAEMON, NDLP_ERR,
444 + "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: "
445 + "receiver disconnected: %s"
446 + , sth->id
447 + , rpt->hostname ? rpt->hostname : "-"
448 + , rpt->client_ip ? rpt->client_ip : "-"
449 + , rpt->client_port ? rpt->client_port : "-"
450 + , why ? why : "");
451 +
452 + internal_fatal(META_GET(&sth->run.meta, (Word_t)&rpt->thread.meta) == NULL, "Receiver to be removed is not found in the list of receivers");
453 + META_DEL(&sth->run.meta, (Word_t)&rpt->thread.meta);
454 +
455 + if(!nd_poll_del(sth->run.ndpl, rpt->sock.fd))
456 + nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to delete receiver socket from nd_poll()");
457 +
458 + rpt->host->stream.rcv.status.tid = 0;
459 +
460 + spinlock_lock(&rpt->thread.send_to_child.spinlock);
461 + rpt->thread.send_to_child.msg.session = 0;
462 + rpt->thread.send_to_child.msg.meta = NULL;
463 + stream_circular_buffer_destroy(rpt->thread.send_to_child.scb);
464 + rpt->thread.send_to_child.scb = NULL;
465 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
466 +
467 + stream_thread_node_removed(rpt->host);
468
469 buffer_free(rpt->thread.buffer);
470 rpt->thread.buffer = NULL;
@@ -388,162 +500,250 @@ static void stream_receiver_on_disconnect(struct stream_thread *sth __maybe_unus
500
501 rrdhost_clear_receiver(rpt);
502 rrdhost_set_is_parent_label();
503 +
504 stream_receiver_free(rpt);
505 + // DO NOT USE rpt after this point
506 }
507
394 -static void stream_receiver_remove(struct stream_thread *sth, struct receiver_state *rpt, const char *why) {
395 - internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
396 -
397 - nd_log(NDLS_DAEMON, NDLP_ERR,
398 - "STREAM RECEIVE[%zu] '%s' [from [%s]:%s]: "
399 - "receiver disconnected: %s"
400 - , sth->id
401 - , rpt->hostname ? rpt->hostname : "-"
402 - , rpt->client_ip ? rpt->client_ip : "-"
403 - , rpt->client_port ? rpt->client_port : "-"
404 - , why ? why : "");
508 +static ssize_t
509 +stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt, PARSER *parser, bool *removed) {
510 + ssize_t rc;
511 + if(rpt->thread.compressed.enabled) {
512 + rc = receiver_read_compressed(rpt);
513 + if(unlikely(rc <= 0))
514 + return rc;
515 +
516 + while(!nd_thread_signaled_to_cancel() && service_running(SERVICE_STREAMING) && !receiver_should_stop(rpt)) {
517 + worker_is_busy(WORKER_STREAM_JOB_DECOMPRESS);
518 +
519 + // feed the decompressor with the new data we just read
520 + decompressor_status_t feed_rc = receiver_feed_decompressor(rpt);
521 +
522 + if(likely(feed_rc == DECOMPRESS_OK)) {
523 + while (true) {
524 + // feed our uncompressed data buffer with new data
525 + decompressor_status_t decompress_rc = receiver_get_decompressed(rpt);
526 +
527 + if (likely(decompress_rc == DECOMPRESS_OK)) {
528 + // loop through all the complete lines found in the uncompressed buffer
529 +
530 + while (buffered_reader_next_line(&rpt->reader, rpt->thread.buffer)) {
531 + if (unlikely(parser_action(parser, rpt->thread.buffer->buffer))) {
532 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
533 + stream_receiver_remove(sth, rpt, "parser action failed");
534 + *removed = true;
535 + return -1;
536 + }
537
406 - internal_fatal(RECEIVERS_GET(&sth->rcv.receivers, (Word_t)rpt) == NULL, "Receiver to be removed is not found in the list of receivers");
407 - RECEIVERS_DEL(&sth->rcv.receivers, (Word_t)rpt);
408 - if(!nd_poll_del(sth->run.ndpl, rpt->sock.fd))
409 - internal_fatal(true, "Failed to remove receiver socket from nd_poll()");
538 + rpt->thread.buffer->len = 0;
539 + rpt->thread.buffer->buffer[0] = '\0';
540 + }
541 + }
542 + else if (decompress_rc == DECOMPRESS_NEED_MORE_DATA)
543 + break;
544 +
545 + else {
546 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
547 + stream_receiver_remove(sth, rpt, "receiver decompressor failed");
548 + *removed = true;
549 + return -1;
550 + }
551 + }
552 + }
553 + else if (feed_rc == DECOMPRESS_NEED_MORE_DATA)
554 + break;
555 + else {
556 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
557 + stream_receiver_remove(sth, rpt, "receiver compressed data invalid");
558 + *removed = true;
559 + return -1;
560 + }
561 + }
562
411 - rpt->host->stream.rcv.status.tid = 0;
563 + if(receiver_should_stop(rpt)) {
564 + receiver_set_exit_reason(rpt, rpt->exit.reason, false);
565 + stream_receiver_remove(sth, rpt, "received stop signal");
566 + *removed = true;
567 + return -1;
568 + }
569 + }
570 + else {
571 + rc = receiver_read_uncompressed(rpt);
572 + if(rc <= 0) return rc;
573 +
574 + while(buffered_reader_next_line(&rpt->reader, rpt->thread.buffer)) {
575 + if(unlikely(parser_action(parser, rpt->thread.buffer->buffer))) {
576 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
577 + stream_receiver_remove(sth, rpt, "parser action failed");
578 + *removed = true;
579 + return -1;
580 + }
581
413 - stream_thread_node_removed(rpt->host);
582 + rpt->thread.buffer->len = 0;
583 + rpt->thread.buffer->buffer[0] = '\0';
584 + }
585 + }
586
415 - stream_receiver_on_disconnect(sth, rpt);
416 - // DO NOT USE rpt after this point
587 + return rc;
588 }
589
590 // process poll() events for streaming receivers
420 -void stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events __maybe_unused, usec_t now_ut) {
421 - PARSER *parser = __atomic_load_n(&rpt->thread.parser, __ATOMIC_RELAXED);
422 - ND_LOG_STACK lgs[] = {
423 - ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->client_ip),
424 - ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->client_port),
425 - ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rpt->hostname),
426 - ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
427 - ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
428 - ND_LOG_FIELD_CB(NDF_REQUEST, line_splitter_reconstruct_line, &parser->line),
429 - ND_LOG_FIELD_CB(NDF_NIDL_NODE, parser_reconstruct_node, parser),
430 - ND_LOG_FIELD_CB(NDF_NIDL_INSTANCE, parser_reconstruct_instance, parser),
431 - ND_LOG_FIELD_CB(NDF_NIDL_CONTEXT, parser_reconstruct_context, parser),
432 - ND_LOG_FIELD_END(),
433 - };
434 - ND_LOG_STACK_PUSH(lgs);
591 +void stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut)
592 +{
593 + internal_fatal(
594 + sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
595
436 - if(receiver_should_stop(rpt)) {
437 - receiver_set_exit_reason(rpt, rpt->exit.reason, false);
438 - stream_receiver_remove(sth, rpt, "received stop signal");
439 - return;
440 - }
441 -
442 - rpt->last_msg_t = (time_t)(now_ut / USEC_PER_SEC);
596 + PARSER *parser = __atomic_load_n(&rpt->thread.parser, __ATOMIC_RELAXED);
597 + ND_LOG_STACK lgs[] = {
598 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->client_ip),
599 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->client_port),
600 + ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rpt->hostname),
601 + ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
602 + ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
603 + ND_LOG_FIELD_CB(NDF_REQUEST, line_splitter_reconstruct_line, &parser->line),
604 + ND_LOG_FIELD_CB(NDF_NIDL_NODE, parser_reconstruct_node, parser),
605 + ND_LOG_FIELD_CB(NDF_NIDL_INSTANCE, parser_reconstruct_instance, parser),
606 + ND_LOG_FIELD_CB(NDF_NIDL_CONTEXT, parser_reconstruct_context, parser),
607 + ND_LOG_FIELD_END(),
608 + };
609 + ND_LOG_STACK_PUSH(lgs);
610
444 - if(rpt->thread.compressed.enabled) {
445 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
611 + if (receiver_should_stop(rpt)) {
612 + receiver_set_exit_reason(rpt, rpt->exit.reason, false);
613 + stream_receiver_remove(sth, rpt, "received stop signal");
614 + return;
615 + }
616
447 - ssize_t bytes = receiver_read_compressed(rpt);
448 - if(unlikely(bytes <= 0)) {
449 - if(bytes < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
450 - return;
617 + if (unlikely(events & (ND_POLL_ERROR | ND_POLL_HUP | ND_POLL_INVALID))) {
618 + // we have errors on this socket
619
452 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_ERROR);
453 - receiver_set_exit_reason(rpt, read_stream_error_to_reason(bytes), false);
454 - stream_receiver_remove(sth, rpt, "receiver socket read error");
455 - return;
456 - }
620 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_ERROR);
621
458 - bool node_removed = false;
459 - while(!node_removed && !nd_thread_signaled_to_cancel() && service_running(SERVICE_STREAMING) && !receiver_should_stop(rpt)) {
460 - worker_is_busy(WORKER_STREAM_JOB_DECOMPRESS);
622 + char *error = "unknown error";
623
462 - // feed the decompressor with the new data we just read
463 - decompressor_status_t feed = receiver_feed_decompressor(rpt);
624 + if (events & ND_POLL_ERROR)
625 + error = "socket reports errors";
626 + else if (events & ND_POLL_HUP)
627 + error = "connection closed by remote end (HUP)";
628 + else if (events & ND_POLL_INVALID)
629 + error = "connection is invalid";
630
465 - if(likely(feed == DECOMPRESS_OK)) {
466 - while (!node_removed) {
467 - // feed our uncompressed data buffer with new data
468 - decompressor_status_t rc = receiver_get_decompressed(rpt);
631 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR);
632
470 - if (likely(rc == DECOMPRESS_OK)) {
471 - // loop through all the complete lines found in the uncompressed buffer
633 + nd_log(
634 + NDLS_DAEMON,
635 + NDLP_ERR,
636 + "STREAM RECEIVE[%zu] %s [from %s]: %s - closing connection",
637 + sth->id,
638 + rrdhost_hostname(rpt->host),
639 + rpt->client_ip,
640 + error);
641
473 - while (buffered_reader_next_line(&rpt->reader, rpt->thread.buffer)) {
474 - if (unlikely(parser_action(parser, rpt->thread.buffer->buffer))) {
475 - receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
476 - stream_receiver_remove(sth, rpt, "parser action failed");
477 - node_removed = true;
478 - break;
479 - }
642 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, false);
643 + stream_receiver_remove(sth, rpt, error);
644 + return;
645 + }
646
481 - rpt->thread.buffer->len = 0;
482 - rpt->thread.buffer->buffer[0] = '\0';
483 - }
484 - }
485 - else if (rc == DECOMPRESS_NEED_MORE_DATA)
486 - break;
487 -
488 - else {
489 - receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
490 - stream_receiver_remove(sth, rpt, "receiver decompressor failed");
491 - node_removed = true;
492 - break;
493 - }
494 - }
647 + if (events & ND_POLL_WRITE) {
648 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
649 +
650 + if (spinlock_trylock(&rpt->thread.send_to_child.spinlock)) {
651 + const char *disconnect_reason = NULL;
652 + STREAM_HANDSHAKE reason;
653 +
654 + char *chunk;
655 + STREAM_CIRCULAR_BUFFER *scb = rpt->thread.send_to_child.scb;
656 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(scb);
657 + size_t outstanding = stream_circular_buffer_get_unsafe(scb, &chunk);
658 + ssize_t rc = write_stream(rpt, chunk, outstanding);
659 + if (likely(rc > 0)) {
660 + stream_circular_buffer_del_unsafe(scb, rc);
661 + if (!stats->bytes_outstanding) {
662 + if (!nd_poll_upd(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
663 + nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM RECEIVE: cannot update nd_poll()");
664 +
665 + // recreate the circular buffer if we have to
666 + stream_circular_buffer_recreate_timed_unsafe(rpt->thread.send_to_child.scb, now_ut, false);
667 }
496 - else if (feed == DECOMPRESS_NEED_MORE_DATA)
497 - break;
668 + } else if (rc == 0 || errno == ECONNRESET) {
669 + disconnect_reason = "socket reports EOF (closed by child)";
670 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
671 + } else if (rc < 0) {
672 + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
673 + // will try later
674 + ;
675 else {
499 - receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
500 - stream_receiver_remove(sth, rpt, "receiver compressed data invalid");
501 - node_removed = true;
502 - break;
676 + disconnect_reason = "socket reports error while writing";
677 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
678 }
679 }
505 -
506 - if(!node_removed && receiver_should_stop(rpt)) {
507 - receiver_set_exit_reason(rpt, rpt->exit.reason, false);
508 - stream_receiver_remove(sth, rpt, "received stop signal");
680 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
681 +
682 + if (disconnect_reason) {
683 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
684 + nd_log(NDLS_DAEMON, NDLP_ERR,
685 + "STREAM RECEIVE[%zu] %s [from %s]: %s (%zd, on fd %d) - closing connection - "
686 + "we have sent %zu bytes in %zu operations.",
687 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, disconnect_reason, rc, rpt->sock.fd,
688 + stats->bytes_sent, stats->sends);
689 +
690 + receiver_set_exit_reason(rpt, reason, false);
691 + stream_receiver_remove(sth, rpt, disconnect_reason);
692 return;
693 }
694 }
512 - else {
513 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
695 + }
696
515 - ssize_t bytes = receiver_read_uncompressed(rpt);
516 - if(unlikely(bytes <= 0)) {
517 - if(bytes < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
518 - return;
697 + if (!(events & ND_POLL_READ))
698 + return;
699
520 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_ERROR);
521 - receiver_set_exit_reason(rpt, read_stream_error_to_reason(bytes), false);
522 - stream_receiver_remove(sth, rpt, "socker read error");
523 - return;
524 - }
700 + // we can receive data from this socket
701
526 - while(buffered_reader_next_line(&rpt->reader, rpt->thread.buffer)) {
527 - if(unlikely(parser_action(parser, rpt->thread.buffer->buffer))) {
528 - receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
529 - stream_receiver_remove(sth, rpt, "parser action failed");
530 - break;
531 - }
702 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
703 + while(true) {
704 + bool removed = false;
705 + ssize_t rc = stream_receive_and_process(sth, rpt, parser, &removed);
706 + if (likely(rc > 0)) {
707 + rpt->last_msg_t = (time_t)(now_ut / USEC_PER_SEC);
708 + }
709 + else if (rc == 0 || errno == ECONNRESET) {
710 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED);
711 + nd_log(NDLS_DAEMON, NDLP_ERR,
712 + "STREAM RECEIVE[%zu] %s [from %s]: socket %d reports EOF (closed by child).",
713 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->sock.fd);
714 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, false);
715 + stream_receiver_remove(sth, rpt, "socket reports EOF (closed by child)");
716 + return;
717 + }
718 + else if (rc < 0) {
719 + if(removed)
720 + return;
721
533 - rpt->thread.buffer->len = 0;
534 - rpt->thread.buffer->buffer[0] = '\0';
722 + else if ((errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
723 + // will try later
724 + break;
725 + else {
726 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
727 + nd_log(NDLS_DAEMON, NDLP_ERR,
728 + "STREAM RECEIVE[%zu] %s [from %s]: error during receive (%zd, on fd %d) - closing connection.",
729 + sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rc, rpt->sock.fd);
730 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, false);
731 + stream_receiver_remove(sth, rpt, "error during receive");
732 + return;
733 }
734 }
735 + }
736 }
737
738 void stream_receiver_cleanup(struct stream_thread *sth) {
739 Word_t idx = 0;
541 - for(struct receiver_state *rpt = RECEIVERS_FIRST(&sth->rcv.receivers, &idx);
542 - rpt;
543 - rpt = RECEIVERS_NEXT(&sth->rcv.receivers, &idx))
740 + for(struct pollfd_meta *m = META_FIRST(&sth->run.meta, &idx);
741 + m;
742 + m = META_NEXT(&sth->run.meta, &idx)) {
743 + if (m->type != POLLFD_TYPE_RECEIVER) continue;
744 + struct receiver_state *rpt = m->rpt;
745 stream_receiver_remove(sth, rpt, "shutdown");
545 -
546 - RECEIVERS_FREE(&sth->rcv.receivers, NULL);
746 + }
747 }
748
749 static void stream_receiver_replication_reset(RRDHOST *host) {
src/streaming/stream-sender-api.c
+5 -4
@@ -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 bool stream_sender_has_capabilities(struct rrdhost *host, STREAM_CAPABILITIES capabilities) {
7 return host && stream_has_capability(host->sender, capabilities);
@@ -32,7 +33,7 @@ void stream_sender_structures_init(RRDHOST *host, bool stream, STRING *parents,
33
34 host->sender->connector.id = -1;
35 host->sender->host = host;
35 - host->sender->sbuf.cb = cbuffer_new(CBUFFER_INITIAL_SIZE, CBUFFER_INITIAL_MAX_SIZE, &netdata_buffers_statistics.cbuffers_streaming);
36 + host->sender->scb = stream_circular_buffer_create();
37 host->sender->capabilities = stream_our_capabilities(host, true);
38
39 nd_sock_init(&host->sender->sock, netdata_ssl_streaming_sender_ctx, netdata_ssl_validate_certificate_sender);
@@ -61,8 +62,8 @@ void stream_sender_structures_free(struct rrdhost *host) {
62
63 // stop a possibly running thread
64 stream_sender_signal_to_stop_and_wait(host, STREAM_HANDSHAKE_DISCONNECT_HOST_CLEANUP, true);
64 - cbuffer_free(host->sender->sbuf.cb);
65 -
65 + stream_circular_buffer_destroy(host->sender->scb);
66 + host->sender->scb = NULL;
67 stream_compressor_destroy(&host->sender->compressor);
68
69 replication_cleanup_sender(host->sender);
@@ -111,7 +112,7 @@ void stream_sender_signal_to_stop_and_wait(struct rrdhost *host, STREAM_HANDSHAK
112 msg.opcode = STREAM_OPCODE_SENDER_STOP_RECEIVER_LEFT;
113 else
114 msg.opcode = STREAM_OPCODE_SENDER_STOP_HOST_CLEANUP;
114 - stream_sender_send_msg_to_dispatcher(host->sender, msg);
115 + stream_sender_send_opcode(host->sender, msg);
116
117 while(wait && rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_SENDER_ADDED))
118 sleep_usec(10 * USEC_PER_MS);
src/streaming/stream-sender-commit.c
+21 -61
@@ -1,8 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-thread.h"
4 -
5 -#define SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3
4 +#include "replication.h"
5
6 static __thread struct sender_buffer commit___thread = { 0 };
7
@@ -67,14 +66,12 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
66 if (unlikely(!src || !src_len))
67 return;
68
70 - size_t total_uncompressed_len = src_len;
71 - size_t total_compressed_len = 0;
72 -
69 stream_sender_lock(s);
70
71 // copy the sequence number of sender buffer recreates, while having our lock
72 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(s->scb);
73 if(commit)
77 - commit->sender_recreates = s->sbuf.recreates;
74 + commit->sender_recreates = stats->recreates;
75
76 if (!s->thread.msg.session) {
77 // the dispatcher is not there anymore - ignore these data
@@ -84,43 +81,14 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
81 return;
82 }
83
87 - if (unlikely(s->sbuf.cb->max_size < (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE)) {
88 - // adaptive sizing of the circular buffer is needed to get this.
89 -
90 - nd_log(
91 - NDLS_DAEMON,
92 - NDLP_NOTICE,
93 - "STREAM %s [send to %s]: max buffer size of %zu is too small "
94 - "for a data message of size %zu. Increasing the max buffer size "
95 - "to %d times the max data message size.",
96 - rrdhost_hostname(s->host),
97 - s->connected_to,
98 - s->sbuf.cb->max_size,
99 - buffer_strlen(wb) + 1,
100 - SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE);
101 -
102 - s->sbuf.cb->max_size = (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE;
84 + if (unlikely(stream_circular_buffer_set_max_size_unsafe(s->scb, src_len, false))) {
85 + // adaptive sizing of the circular buffer
86 + 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);
89 }
90
105 -#ifdef NETDATA_LOG_STREAM_SENDER
106 - if (type == STREAM_TRAFFIC_TYPE_METADATA) {
107 - if (!s->stream_log_fp) {
108 - char filename[FILENAME_MAX + 1];
109 - snprintfz(
110 - filename, FILENAME_MAX, "/tmp/stream-sender-%s.txt", s->host ? rrdhost_hostname(s->host) : "unknown");
111 -
112 - s->stream_log_fp = fopen(filename, "w");
113 - }
114 -
115 - fprintf(
116 - s->stream_log_fp,
117 - "\n--- SEND MESSAGE START: %s ----\n"
118 - "%s"
119 - "--- SEND MESSAGE END ----------------------------------------\n",
120 - rrdhost_hostname(s->host),
121 - src);
122 - }
123 -#endif
91 + stream_sender_log_payload(s, wb, type, false);
92
93 if (s->compressor.initialized) {
94 // compressed traffic
@@ -171,15 +139,13 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
139 size_t decoded_dst_len = stream_decompress_decode_signature((const char *)&signature, sizeof(signature));
140 if (decoded_dst_len != dst_len)
141 fatal(
174 - "RRDPUSH COMPRESSION: invalid signature, original payload %zu bytes, "
142 + "STREAM COMPRESSION: invalid signature, original payload %zu bytes, "
143 "compressed payload length %zu bytes, but signature says payload is %zu bytes",
144 size_to_compress, dst_len, decoded_dst_len);
145 #endif
146
179 - total_compressed_len += dst_len + sizeof(signature);
180 -
181 - if (cbuffer_add_unsafe(s->sbuf.cb, (const char *)&signature, sizeof(signature)) ||
182 - cbuffer_add_unsafe(s->sbuf.cb, dst, dst_len))
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))
149 goto overflow_with_lock;
150
151 src = src + size_to_compress;
@@ -189,15 +155,12 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
155 else {
156 // uncompressed traffic
157
192 - total_compressed_len = src_len;
193 -
194 - if (cbuffer_add_unsafe(s->sbuf.cb, src, src_len))
158 + if (!stream_circular_buffer_add_unsafe(s->scb, src, src_len, src_len, type))
159 goto overflow_with_lock;
160 }
161
198 - // update s->dispatcher entries
199 - bool enable_sending = s->thread.bytes_outstanding == 0;
200 - stream_sender_thread_data_added_data_unsafe(s, type, total_compressed_len, total_uncompressed_len);
162 + bool enable_sending = stats->bytes_outstanding == 0;
163 + replication_recalculate_buffer_used_ratio_unsafe(s);
164
165 if (enable_sending)
166 msg = s->thread.msg;
@@ -206,33 +169,30 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
169
170 if (enable_sending) {
171 msg.opcode = STREAM_OPCODE_SENDER_POLLOUT;
209 - stream_sender_send_msg_to_dispatcher(s, msg);
172 + stream_sender_send_opcode(s, msg);
173 }
174
175 return;
176
177 overflow_with_lock: {
215 - size_t buffer_size = s->sbuf.cb->size;
216 - size_t buffer_max_size = s->sbuf.cb->max_size;
217 - size_t buffer_available = cbuffer_available_size_unsafe(s->sbuf.cb);
178 msg = s->thread.msg;
179 stream_sender_unlock(s);
180 msg.opcode = STREAM_OPCODE_SENDER_BUFFER_OVERFLOW;
221 - stream_sender_send_msg_to_dispatcher(s, msg);
181 + stream_sender_send_opcode(s, msg);
182 nd_log(NDLS_DAEMON, NDLP_ERR,
223 - "STREAM %s [send to %s]: buffer overflow while adding %zu bytes (buffer size %zu, max size %zu, available %zu). "
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,
226 - total_compressed_len, buffer_size, buffer_max_size, buffer_available);
186 + stats->bytes_size, stats->bytes_max_size, stats->bytes_outstanding, stats->bytes_available);
187 return;
188 }
189
190 compression_failed_with_lock: {
231 - stream_compression_deactivate(s);
191 + stream_compression_deactivate(s);
192 msg = s->thread.msg;
193 stream_sender_unlock(s);
194 msg.opcode = STREAM_OPCODE_SENDER_RECONNECT_WITHOUT_COMPRESSION;
235 - stream_sender_send_msg_to_dispatcher(s, msg);
195 + 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);
src/streaming/stream-sender-execute.c
+34
@@ -1,6 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-thread.h"
4 +#include "replication.h"
5
6 struct inflight_stream_function {
7 struct sender_state *sender;
@@ -151,6 +152,11 @@ void stream_sender_execute_commands(struct sender_state *s) {
152 };
153 ND_LOG_STACK_PUSH(lgs);
154
155 +#ifdef NETDATA_LOG_STREAM_SENDER
156 + if(!s->log.received)
157 + s->log.received = buffer_create(0, NULL);
158 +#endif
159 +
160 char *start = s->rbuf.b, *end = &s->rbuf.b[s->rbuf.read_len], *newline;
161 *end = '\0';
162 for( ; start < end ; start = newline + 1) {
@@ -169,6 +175,13 @@ void stream_sender_execute_commands(struct sender_state *s) {
175
176 if(s->defer.end_keyword) {
177 if(strcmp(start, s->defer.end_keyword) == 0) {
178 +#ifdef NETDATA_LOG_STREAM_SENDER
179 + buffer_strcat(s->log.received, buffer_tostring(s->defer.payload));
180 + buffer_strcat(s->log.received, "\n");
181 + buffer_strcat(s->log.received, s->defer.end_keyword);
182 + buffer_strcat(s->log.received, "\n");
183 + stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_METADATA, true);
184 +#endif
185 s->defer.action(s, s->defer.action_data);
186 cleanup_deferred_data(s);
187 }
@@ -180,10 +193,19 @@ void stream_sender_execute_commands(struct sender_state *s) {
193 continue;
194 }
195
196 +#ifdef NETDATA_LOG_STREAM_SENDER
197 + buffer_reset(s->log.received);
198 + buffer_strcat(s->log.received, start);
199 + buffer_strcat(s->log.received, "\n");
200 +#endif
201 +
202 s->rbuf.line.num_words = quoted_strings_splitter_whitespace(start, s->rbuf.line.words, PLUGINSD_MAX_WORDS);
203 const char *command = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 0);
204
205 if(command && strcmp(command, PLUGINSD_CALL_FUNCTION) == 0) {
206 +#ifdef NETDATA_LOG_STREAM_SENDER
207 + stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_FUNCTIONS, true);
208 +#endif
209 char *transaction = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 1);
210 char *timeout_s = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 2);
211 char *function = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 3);
@@ -217,6 +239,9 @@ void stream_sender_execute_commands(struct sender_state *s) {
239 }
240 else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_CANCEL) == 0) {
241 worker_is_busy(WORKER_SENDER_JOB_EXECUTE_FUNCTION);
242 +#ifdef NETDATA_LOG_STREAM_SENDER
243 + stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_FUNCTIONS, true);
244 +#endif
245 nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
246
247 char *transaction = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 1);
@@ -225,6 +250,9 @@ void stream_sender_execute_commands(struct sender_state *s) {
250 }
251 else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_PROGRESS) == 0) {
252 worker_is_busy(WORKER_SENDER_JOB_EXECUTE_FUNCTION);
253 +#ifdef NETDATA_LOG_STREAM_SENDER
254 + stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_FUNCTIONS, true);
255 +#endif
256 nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
257
258 char *transaction = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 1);
@@ -233,6 +261,9 @@ void stream_sender_execute_commands(struct sender_state *s) {
261 }
262 else if (command && strcmp(command, PLUGINSD_KEYWORD_REPLAY_CHART) == 0) {
263 worker_is_busy(WORKER_SENDER_JOB_EXECUTE_REPLAY);
264 +#ifdef NETDATA_LOG_STREAM_SENDER
265 + stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_REPLICATION, true);
266 +#endif
267
268 // do not log replication commands received - way too many!
269 // nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
@@ -262,6 +293,9 @@ void stream_sender_execute_commands(struct sender_state *s) {
293 }
294 else if(command && strcmp(command, PLUGINSD_KEYWORD_NODE_ID) == 0) {
295 worker_is_busy(WORKER_SENDER_JOB_EXECUTE_META);
296 +#ifdef NETDATA_LOG_STREAM_SENDER
297 + stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_METADATA, true);
298 +#endif
299 stream_sender_get_node_and_claim_id_from_parent(s);
300 }
301 else if(command && strcmp(command, PLUGINSD_KEYWORD_JSON) == 0) {
src/streaming/stream-sender-internals.h
+16 -40
@@ -8,6 +8,7 @@
8 #include "h2o-common.h"
9 #include "aclk/https_client.h"
10 #include "stream-parents.h"
11 +#include "stream-circular-buffer.h"
12
13 // connector thread
14 #define WORKER_SENDER_CONNECTOR_JOB_CONNECTING 0
@@ -22,10 +23,6 @@
23
24 #define CONNECTED_TO_SIZE 100
25
25 -#define CBUFFER_INITIAL_SIZE (16 * 1024)
26 -#define CBUFFER_INITIAL_MAX_SIZE (10 * 1024 * 1024)
27 -#define THREAD_BUFFER_INITIAL_SIZE (CBUFFER_INITIAL_SIZE / 2)
28 -
26 #include "stream-compression/compression.h"
27 #include "stream-conf.h"
28
@@ -48,23 +45,7 @@ struct sender_state {
45 // this is a property of stream_sender_send_msg_to_dispatcher()
46 // protected by dispatcher->messages.spinlock
47 // DO NOT READ OR WRITE ANYWHERE
51 - uint32_t msg_slot; // ensures a dispatcher queue that can never get full
52 -
53 - // statistics about our compression efficiency
54 - size_t bytes_compressed;
55 - size_t bytes_uncompressed;
56 -
57 - // the current buffer statistics
58 - // these SHOULD ALWAYS BE CALCULATED ON EVERY stream_sender_unlock() IF THE BUFFER WAS MODIFIED
59 - // stream_sender_lock() IS REQUIRED TO READ/WRITE THESE
60 - size_t bytes_outstanding;
61 - size_t bytes_available;
62 - NETDATA_DOUBLE buffer_ratio;
63 -
64 - // statistics about successful sends
65 - size_t sends;
66 - size_t bytes_sent;
67 - size_t bytes_sent_by_type[STREAM_TRAFFIC_TYPE_MAX];
48 + uint32_t msg_slot; // ensures a opcode queue that can never get full
49
50 usec_t last_traffic_ut;
51
@@ -78,10 +59,7 @@ struct sender_state {
59 char connected_to[CONNECTED_TO_SIZE + 1]; // We don't know which proxy we connect to, passed back from socket.c
60 time_t last_state_since_t; // the timestamp of the last state (online/offline) change
61
81 - struct {
82 - struct circular_buffer *cb;
83 - size_t recreates;
84 - } sbuf;
62 + STREAM_CIRCULAR_BUFFER *scb;
63
64 struct {
65 char b[PLUGINSD_LINE_MAX + 1];
@@ -92,7 +70,11 @@ struct sender_state {
70 struct compressor_state compressor;
71
72 #ifdef NETDATA_LOG_STREAM_SENDER
95 - FILE *stream_log_fp;
73 + struct {
74 + SPINLOCK spinlock;
75 + BUFFER *received;
76 + FILE *fp;
77 + } log;
78 #endif
79
80 struct {
@@ -113,11 +95,6 @@ struct sender_state {
95
96 } replication;
97
116 - struct {
117 - size_t buffer_used_percentage; // the current utilization of the sending buffer
118 - usec_t last_flush_time_ut; // the last time the sender flushed the sending buffer in USEC
119 - } atomic;
120 -
98 struct {
99 const char *end_keyword;
100 BUFFER *payload;
@@ -131,16 +108,11 @@ struct sender_state {
108
109 #define stream_sender_lock(sender) spinlock_lock(&(sender)->spinlock)
110 #define stream_sender_unlock(sender) spinlock_unlock(&(sender)->spinlock)
111 +#define stream_sender_trylock(sender) spinlock_trylock(&(sender)->spinlock)
112
113 #define stream_sender_replication_buffer_full_set(sender, value) __atomic_store_n(&((sender)->replication.atomic.reached_max), value, __ATOMIC_SEQ_CST)
114 #define stream_sender_replication_buffer_full_get(sender) __atomic_load_n(&((sender)->replication.atomic.reached_max), __ATOMIC_SEQ_CST)
115
138 -#define stream_sender_set_buffer_used_percent(sender, value) __atomic_store_n(&((sender)->atomic.buffer_used_percentage), value, __ATOMIC_RELAXED)
139 -#define stream_sender_get_buffer_used_percent(sender) __atomic_load_n(&((sender)->atomic.buffer_used_percentage), __ATOMIC_RELAXED)
140 -
141 -#define stream_sender_set_flush_time(sender) __atomic_store_n(&((sender)->atomic.last_flush_time_ut), now_realtime_usec(), __ATOMIC_RELAXED)
142 -#define stream_sender_get_flush_time(sender) __atomic_load_n(&((sender)->atomic.last_flush_time_ut), __ATOMIC_RELAXED)
143 -
116 #define stream_sender_replicating_charts(sender) __atomic_load_n(&((sender)->replication.atomic.charts_replicating), __ATOMIC_RELAXED)
117 #define stream_sender_replicating_charts_plus_one(sender) __atomic_add_fetch(&((sender)->replication.atomic.charts_replicating), 1, __ATOMIC_RELAXED)
118 #define stream_sender_replicating_charts_minus_one(sender) __atomic_sub_fetch(&((sender)->replication.atomic.charts_replicating), 1, __ATOMIC_RELAXED)
@@ -160,9 +132,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
132
133 bool stream_sender_is_host_stopped(struct sender_state *s);
134
163 -void stream_sender_send_msg_to_dispatcher(struct sender_state *s, struct stream_opcode msg);
164 -
165 -void stream_sender_thread_data_added_data_unsafe(struct sender_state *s, STREAM_TRAFFIC_TYPE type, uint64_t bytes_compressed, uint64_t bytes_uncompressed);
135 +void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg);
136
137 void stream_sender_add_to_queue(struct sender_state *s);
138
@@ -177,4 +147,10 @@ void stream_sender_on_connect(struct sender_state *s);
147
148 void stream_sender_remove(struct sender_state *s);
149
150 +#ifdef NETDATA_LOG_STREAM_SENDER
151 +void stream_sender_log_payload(struct sender_state *s, BUFFER *payload, STREAM_TRAFFIC_TYPE type, bool inbound);
152 +#else
153 +#define stream_sender_log_payload(s, payload, type, inbound) debug_dummy()
154 +#endif
155 +
156 #endif //NETDATA_STREAM_SENDER_INTERNALS_H
src/streaming/stream-sender.c
+174 -163
@@ -2,44 +2,48 @@
2
3 #include "stream-thread.h"
4 #include "stream-sender-internals.h"
5 +#include "replication.h"
6
7 static void stream_sender_move_running_to_connector_or_remove(struct stream_thread *sth, struct sender_state *s, STREAM_HANDSHAKE reason, bool reconnect);
8
9 // --------------------------------------------------------------------------------------------------------------------
10
10 -static void stream_sender_cbuffer_recreate_timed_unsafe(struct sender_state *s, usec_t now_ut, bool force) {
11 - static __thread usec_t last_reset_time_ut = 0;
12 -
13 - if(!force && now_ut - last_reset_time_ut < 300 * USEC_PER_SEC)
14 - return;
15 -
16 - last_reset_time_ut = now_ut;
11 +#ifdef NETDATA_LOG_STREAM_SENDER
12 +void stream_sender_log_payload(struct sender_state *s, BUFFER *payload, STREAM_TRAFFIC_TYPE type __maybe_unused, bool inbound) {
13 + spinlock_lock(&s->log.spinlock);
14
18 - s->sbuf.recreates++; // we increase even if we don't do it, to have sender_start() recreate its buffers
15 + if (!s->log.fp) {
16 + char filename[FILENAME_MAX + 1];
17 + snprintfz(
18 + filename, FILENAME_MAX, "/tmp/stream-sender-%s.txt", s->host ? rrdhost_hostname(s->host) : "unknown");
19
20 - if(s->sbuf.cb && s->sbuf.cb->size > CBUFFER_INITIAL_SIZE) {
21 - cbuffer_free(s->sbuf.cb);
22 - s->sbuf.cb = cbuffer_new(CBUFFER_INITIAL_SIZE, stream_send.buffer_max_size, &netdata_buffers_statistics.cbuffers_streaming);
20 + s->log.fp = fopen(filename, "w");
21 }
24 -}
25 -
26 -static void rrdpush_sender_cbuffer_flush(RRDHOST *host) {
27 - stream_sender_set_flush_time(host->sender);
22
29 - stream_sender_lock(host->sender);
30 -
31 - // flush the output buffer from any data it may have
32 - cbuffer_flush(host->sender->sbuf.cb);
33 - stream_sender_cbuffer_recreate_timed_unsafe(host->sender, now_monotonic_usec(), true);
23 + if(inbound) {
24 + fprintf(
25 + s->log.fp,
26 + "\n--- RECEIVE MESSAGE START: %s => %s ----\n"
27 + "%s"
28 + "--- RECEIVE MESSAGE END ----------------------------------------\n",
29 + s->connected_to, rrdhost_hostname(s->host), buffer_tostring(payload));
30 + }
31 + else {
32 + fprintf(
33 + s->log.fp,
34 + "\n--- SEND MESSAGE START: %s => %s ----\n"
35 + "%s"
36 + "--- SEND MESSAGE END ----------------------------------------\n",
37 + rrdhost_hostname(s->host), s->connected_to, buffer_tostring(payload));
38 + }
39
35 - stream_sender_unlock(host->sender);
40 + spinlock_unlock(&s->log.spinlock);
41 }
42 +#endif
43
44 // --------------------------------------------------------------------------------------------------------------------
45
40 -static void rrdpush_sender_charts_and_replication_reset(struct sender_state *s) {
41 - stream_sender_set_flush_time(s);
42 -
46 +static void stream_sender_charts_and_replication_reset(struct sender_state *s) {
47 // stop all replication commands inflight
48 replication_sender_delete_pending_requests(s);
49
@@ -73,13 +77,14 @@ void stream_sender_on_connect(struct sender_state *s) {
77
78 rrdhost_flag_set(s->host, RRDHOST_FLAG_STREAM_SENDER_CONNECTED);
79
76 - rrdpush_sender_charts_and_replication_reset(s);
77 - rrdpush_sender_cbuffer_flush(s->host);
80 + stream_sender_charts_and_replication_reset(s);
81 +
82 + stream_sender_lock(s);
83 + stream_circular_buffer_flush_unsafe(s->scb, stream_send.buffer_max_size);
84 + stream_sender_unlock(s);
85
86 s->thread.last_traffic_ut = now_monotonic_usec();
87 s->rbuf.read_len = 0;
81 - s->sbuf.cb->read = 0;
82 - s->sbuf.cb->write = 0;
88 }
89
90 static void stream_sender_on_ready_to_dispatch(struct sender_state *s) {
@@ -103,8 +108,12 @@ static void stream_sender_on_disconnect(struct sender_state *s) {
108 "STREAM SEND [%s]: running on-disconnect hooks...",
109 rrdhost_hostname(s->host));
110
111 + stream_sender_lock(s);
112 + stream_circular_buffer_flush_unsafe(s->scb, stream_send.buffer_max_size);
113 + stream_sender_unlock(s);
114 +
115 stream_sender_execute_commands_cleanup(s);
107 - rrdpush_sender_charts_and_replication_reset(s);
116 + stream_sender_charts_and_replication_reset(s);
117 stream_sender_clear_parent_claim_id(s->host);
118 stream_receiver_send_node_and_claim_id_to_child(s->host);
119 stream_path_parent_disconnected(s->host);
@@ -150,42 +159,6 @@ static bool stream_sender_log_dst_port(BUFFER *wb, void *ptr) {
159 return true;
160 }
161
153 -// --------------------------------------------------------------------------------------------------------------------
154 -
155 -static void stream_sender_thread_data_reset_unsafe(struct sender_state *s) {
156 - memset(s->thread.bytes_sent_by_type, 0, sizeof(s->thread.bytes_sent_by_type));
157 -
158 - s->thread.bytes_uncompressed = 0;
159 - s->thread.bytes_compressed = 0;
160 - s->thread.bytes_outstanding = 0;
161 - s->thread.bytes_available = 0;
162 - s->thread.buffer_ratio = 0.0;
163 - s->thread.sends = 0;
164 - s->thread.bytes_sent = 0;
165 - replication_recalculate_buffer_used_ratio_unsafe(s);
166 -}
167 -
168 -static void stream_sender_thread_data_sent_data_unsafe(struct sender_state *s, uint64_t bytes_sent) {
169 - s->thread.sends++;
170 - s->thread.bytes_sent += bytes_sent;
171 - s->thread.bytes_outstanding = cbuffer_next_unsafe(s->sbuf.cb, NULL);
172 - s->thread.bytes_available = cbuffer_available_size_unsafe(s->sbuf.cb);
173 - s->thread.buffer_ratio = (NETDATA_DOUBLE)(s->sbuf.cb->max_size - s->thread.bytes_available) * 100.0 / (NETDATA_DOUBLE)s->sbuf.cb->max_size;
174 - replication_recalculate_buffer_used_ratio_unsafe(s);
175 -}
176 -
177 -void stream_sender_thread_data_added_data_unsafe(struct sender_state *s, STREAM_TRAFFIC_TYPE type, uint64_t bytes_compressed, uint64_t bytes_uncompressed) {
178 - // calculate the statistics for our dispatcher
179 - s->thread.bytes_sent_by_type[type] += bytes_compressed;
180 -
181 - s->thread.bytes_uncompressed += bytes_uncompressed;
182 - s->thread.bytes_compressed += bytes_compressed;
183 - s->thread.bytes_outstanding = cbuffer_next_unsafe(s->sbuf.cb, NULL);
184 - s->thread.bytes_available = cbuffer_available_size_unsafe(s->sbuf.cb);
185 - s->thread.buffer_ratio = (NETDATA_DOUBLE)(s->sbuf.cb->max_size - s->thread.bytes_available) * 100.0 / (NETDATA_DOUBLE)s->sbuf.cb->max_size;
186 - replication_recalculate_buffer_used_ratio_unsafe(s);
187 -}
188 -
162 // --------------------------------------------------------------------------------------------------------------------
163 // opcodes
164
@@ -195,7 +168,7 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
168 ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
169 ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
170 ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
198 - ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_sender_log_capabilities, s),
171 + ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
172 ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
173 ND_LOG_FIELD_END(),
174 };
@@ -205,18 +178,17 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
178 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW);
179 errno_clear();
180 stream_sender_lock(s);
208 - size_t buffer_size = s->sbuf.cb->size;
209 - size_t buffer_max_size = s->sbuf.cb->max_size;
210 - size_t buffer_available = cbuffer_available_size_unsafe(s->sbuf.cb);
181 + // copy the statistics
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,
213 - "STREAM SEND[%zu] %s [to %s]: send buffer is full (buffer size %zu, max %zu, available %zu). "
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,
216 - buffer_size, buffer_max_size, buffer_available);
188 + stats.bytes_size, stats.bytes_max_size, stats.bytes_outstanding, stats.bytes_available);
189
190 stream_sender_move_running_to_connector_or_remove(
219 - sth, s, STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SENDER_SEND_BUFFER, true);
191 + sth, s, STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SEND_BUFFER, true);
192 return;
193 }
194
@@ -276,26 +248,28 @@ void stream_sender_move_queue_to_running_unsafe(struct stream_thread *sth) {
248 "STREAM SEND[%zu] [%s]: moving host from dispatcher queue to dispatcher running...",
249 sth->id, rrdhost_hostname(s->host));
250
279 - internal_fatal(SENDERS_GET(&sth->snd.senders, (Word_t)s) != NULL, "Sender already exists in senders list");
280 - SENDERS_SET(&sth->snd.senders, (Word_t)s, s);
281 -
251 stream_sender_lock(s);
252 s->thread.meta.type = POLLFD_TYPE_SENDER;
253 s->thread.meta.s = s;
285 - if(!nd_poll_add(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
286 - internal_fatal(true, "Failed to add sender socket to nd_poll()");
254
255 s->thread.msg.thread_slot = (int32_t)sth->id;
256 s->thread.msg.session = os_random32();
290 - s->thread.msg.sender = s;
257 + s->thread.msg.meta = &s->thread.meta;
258
259 s->host->stream.snd.status.tid = gettid_cached();
260 s->host->stream.snd.status.connections++;
261 s->last_state_since_t = now_realtime_sec();
262
296 - stream_sender_thread_data_reset_unsafe(s);
263 + stream_circular_buffer_flush_unsafe(s->scb, stream_send.buffer_max_size);
264 + replication_recalculate_buffer_used_ratio_unsafe(s);
265 stream_sender_unlock(s);
266
267 + internal_fatal(META_GET(&sth->run.meta, (Word_t)&s->thread.meta) != NULL, "Sender already exists in meta list");
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()");
272 +
273 stream_sender_on_ready_to_dispatch(s);
274 }
275 }
@@ -324,20 +298,25 @@ void stream_sender_remove(struct sender_state *s) {
298 rrdhost_stream_parents_reset(s->host, STREAM_HANDSHAKE_EXITING);
299
300 #ifdef NETDATA_LOG_STREAM_SENDER
327 - if (s->stream_log_fp) {
328 - fclose(s->stream_log_fp);
329 - s->stream_log_fp = NULL;
301 + spinlock_lock(&s->log.spinlock);
302 + if (s->log.fp) {
303 + fclose(s->log.fp);
304 + s->log.fp = NULL;
305 }
306 + buffer_free(s->log.received);
307 + s->log.received = NULL;
308 + spinlock_unlock(&s->log.spinlock);
309 #endif
310 }
311
312 static void stream_sender_move_running_to_connector_or_remove(struct stream_thread *sth, struct sender_state *s, STREAM_HANDSHAKE reason, bool reconnect) {
313 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
314
337 - internal_fatal(SENDERS_GET(&sth->snd.senders, (Word_t)s) == NULL, "Sender to be removed is not in the list of senders");
338 - SENDERS_DEL(&sth->snd.senders, (Word_t)s);
315 + internal_fatal(META_GET(&sth->run.meta, (Word_t)&s->thread.meta) == NULL, "Sender to be removed is not in the list of senders");
316 + META_DEL(&sth->run.meta, (Word_t)&s->thread.meta);
317 +
318 if(!nd_poll_del(sth->run.ndpl, s->sock.fd))
340 - internal_fatal(true, "Failed to remove sender socket from nd_poll()");
319 + nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to delete sender socket from nd_poll()");
320
321 // clear this flag asap, to stop other threads from pushing metrics for this node
322 rrdhost_flag_clear(s->host, RRDHOST_FLAG_STREAM_SENDER_CONNECTED | RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS);
@@ -346,7 +325,7 @@ static void stream_sender_move_running_to_connector_or_remove(struct stream_thre
325 stream_sender_lock(s);
326
327 s->thread.msg.session = 0;
349 - s->thread.msg.sender = NULL;
328 + s->thread.msg.meta = NULL;
329
330 s->host->stream.snd.status.tid = 0;
331 stream_sender_unlock(s);
@@ -378,19 +357,22 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
357 NETDATA_DOUBLE overall_buffer_ratio = 0.0;
358
359 Word_t idx = 0;
381 - for(struct sender_state *s = SENDERS_FIRST(&sth->snd.senders, &idx);
382 - s;
383 - s = SENDERS_NEXT(&sth->snd.senders, &idx)) {
360 + for(struct pollfd_meta *m = META_FIRST(&sth->run.meta, &idx);
361 + m;
362 + m = META_NEXT(&sth->run.meta, &idx)) {
363 + if(m->type != POLLFD_TYPE_SENDER) continue;
364 + struct sender_state *s = m->s;
365
366 stream_sender_lock(s);
386 - size_t outstanding = cbuffer_next_unsafe(s->sbuf.cb, NULL);
387 - NETDATA_DOUBLE buffer_ratio = s->thread.buffer_ratio;
367 + // copy the statistics
368 + STREAM_CIRCULAR_BUFFER_STATS stats = *stream_circular_buffer_stats_unsafe(s->scb);
369 stream_sender_unlock(s);
370
390 - if (buffer_ratio > overall_buffer_ratio)
391 - overall_buffer_ratio = buffer_ratio;
371 + if (stats.buffer_ratio > overall_buffer_ratio)
372 + overall_buffer_ratio = stats.buffer_ratio;
373
393 - if(unlikely(s->thread.last_traffic_ut + stream_send.parents.timeout_s * USEC_PER_SEC < now_ut &&
374 + if(unlikely(stats.bytes_outstanding &&
375 + s->thread.last_traffic_ut + stream_send.parents.timeout_s * USEC_PER_SEC < now_ut &&
376 !stream_sender_pending_replication_requests(s) &&
377 !stream_sender_replicating_charts(s)
378 )) {
@@ -408,27 +390,29 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
390
391 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
392
411 - char since[RFC3339_MAX_LENGTH];
412 - rfc3339_datetime_ut(since, sizeof(since), s->thread.last_traffic_ut, 2, false);
393 + char duration[RFC3339_MAX_LENGTH];
394 + duration_snprintf(duration, sizeof(duration), (int64_t)(now_monotonic_usec() - s->thread.last_traffic_ut), "us", true);
395
414 - char pending[64];
415 - size_snprintf(pending, sizeof(pending), outstanding, "B", false);
396 + char pending[64] = "0";
397 + if(stats.bytes_outstanding)
398 + size_snprintf(pending, sizeof(pending), stats.bytes_outstanding, "B", false);
399
400 nd_log(NDLS_DAEMON, NDLP_ERR,
401 "STREAM SEND[%zu] %s [send to %s]: could not send data for %ld seconds - closing connection - "
419 - "we have sent %zu bytes in %zu operations, it is idle since: %s, and we have %s pending to send "
402 + "we have sent %zu bytes in %zu operations, it is idle for %s, and we have %s pending to send "
403 "(buffer is used %.2f%%).",
404 sth->id, rrdhost_hostname(s->host), s->connected_to, stream_send.parents.timeout_s,
422 - s->thread.bytes_sent, s->thread.sends, since, pending, buffer_ratio);
405 + stats.bytes_sent, stats.sends,
406 + duration, pending, stats.buffer_ratio);
407
408 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_TIMEOUT, true);
409 continue;
410 }
411
428 - bytes_compressed += s->thread.bytes_compressed;
429 - bytes_uncompressed += s->thread.bytes_uncompressed;
412 + bytes_compressed += stats.bytes_added;
413 + bytes_uncompressed += stats.bytes_uncompressed;
414
431 - if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ | (outstanding ? ND_POLL_WRITE : 0), &s->thread.meta))
415 + if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ | (stats.bytes_outstanding ? ND_POLL_WRITE : 0), &s->thread.meta))
416 nd_log(NDLS_DAEMON, NDLP_ERR,
417 "STREAM SEND[%zu] %s [send to %s]: failed to update nd_poll().",
418 sth->id, rrdhost_hostname(s->host), s->connected_to);
@@ -458,7 +442,7 @@ void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
442 };
443 ND_LOG_STACK_PUSH(lgs);
444
461 - if(unlikely(events & ND_POLL_ERROR)) {
445 + if(unlikely(events & (ND_POLL_ERROR|ND_POLL_HUP|ND_POLL_INVALID))) {
446 // we have errors on this socket
447
448 worker_is_busy(WORKER_STREAM_JOB_SOCKET_ERROR);
@@ -474,9 +458,14 @@ void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
458
459 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR);
460
461 + stream_sender_lock(s);
462 + // copy the statistics
463 + STREAM_CIRCULAR_BUFFER_STATS stats = *stream_circular_buffer_stats_unsafe(s->scb);
464 + stream_sender_unlock(s);
465 +
466 nd_log(NDLS_DAEMON, NDLP_ERR,
478 - "STREAM SEND[%zu] %s [send to %s]: %s restarting connection - %zu bytes transmitted.",
479 - sth->id, rrdhost_hostname(s->host), s->connected_to, error, s->thread.bytes_sent);
467 + "STREAM SEND[%zu] %s [to %s]: %s restarting connection - %zu bytes transmitted in %zu operations.",
468 + sth->id, rrdhost_hostname(s->host), s->connected_to, error, stats.bytes_sent, stats.sends);
469
470 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, true);
471 return;
@@ -485,90 +474,116 @@ void stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
474 if(events & ND_POLL_WRITE) {
475 // we can send data on this socket
476
488 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
477 + if(stream_sender_trylock(s)) {
478 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
479
490 - bool disconnect = false;
491 - stream_sender_lock(s);
492 - {
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;
494 - size_t outstanding = cbuffer_next_unsafe(s->sbuf.cb, &chunk);
495 - ssize_t bytes = nd_sock_send_nowait(&s->sock, chunk, outstanding);
496 - if (likely(bytes > 0)) {
497 - cbuffer_remove_unsafe(s->sbuf.cb, bytes);
498 - stream_sender_thread_data_sent_data_unsafe(s, bytes);
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;
500 - sth->snd.bytes_sent += bytes;
491 + sth->snd.bytes_sent += rc;
492
502 - if(!s->thread.bytes_outstanding) {
493 + if (!stats->bytes_outstanding) {
494 // we sent them all - remove ND_POLL_WRITE
504 - if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
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
510 - stream_sender_cbuffer_recreate_timed_unsafe(s, now_ut, false);
501 + stream_circular_buffer_recreate_timed_unsafe(s->scb, now_ut, false);
502 }
503 }
513 - else if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
514 - disconnect = true;
515 - }
516 - stream_sender_unlock(s);
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 + }
516 + }
517 + stream_sender_unlock(s);
518
518 - if(disconnect) {
519 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
520 - nd_log(NDLS_DAEMON, NDLP_ERR,
521 - "STREAM SEND[%zu] %s [send to %s]: failed to send metrics - restarting connection - "
522 - "we have sent %zu bytes on this connection.",
523 - sth->id, rrdhost_hostname(s->host), s->connected_to, s->thread.bytes_sent);
524 - stream_sender_move_running_to_connector_or_remove(
525 - sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED, true);
526 - return;
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 + }
531 }
532 }
533
530 - if(events & ND_POLL_READ) {
531 - // we can receive data from this socket
534 + if(!(events & ND_POLL_READ))
535 + return;
536 +
537 + // we can receive data from this socket
538 +
539 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
540 + while(true) {
541 + // we have to drain the socket!
542 +
543 + 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);
544 + if (likely(rc > 0)) {
545 + s->rbuf.read_len += rc;
546
533 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
534 - ssize_t bytes = nd_sock_revc_nowait(&s->sock, s->rbuf.b + s->rbuf.read_len, sizeof(s->rbuf.b) - s->rbuf.read_len - 1);
535 - if (bytes > 0) {
536 - s->rbuf.read_len += bytes;
547 s->thread.last_traffic_ut = now_ut;
538 - sth->snd.bytes_received += bytes;
548 + sth->snd.bytes_received += rc;
549 +
550 + worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
551 + stream_sender_execute_commands(s);
552 }
540 - else if (bytes == 0 || errno == ECONNRESET) {
541 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED);
553 + else if (rc == 0 || errno == ECONNRESET) {
554 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED);
555 nd_log(NDLS_DAEMON, NDLP_ERR,
543 - "STREAM SEND[%zu] %s [send to %s]: connection (fd %d) closed by far end.",
556 + "STREAM SEND[%zu] %s [to %s]: socket %d reports EOF (closed by parent).",
557 sth->id, rrdhost_hostname(s->host), s->connected_to, s->sock.fd);
558 stream_sender_move_running_to_connector_or_remove(
546 - sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_PARENT, true);
559 + sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, true);
560 return;
561 }
549 - else if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) {
550 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
551 - nd_log(NDLS_DAEMON, NDLP_ERR,
552 - "STREAM SEND[%zu] %s [send to %s]: error during receive (%zd, on fd %d) - restarting connection.",
553 - sth->id, rrdhost_hostname(s->host), s->connected_to, bytes, s->sock.fd);
554 - stream_sender_move_running_to_connector_or_remove(
555 - sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, true);
556 - return;
562 + else if (rc < 0) {
563 + if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
564 + // will try later
565 + break;
566 + else {
567 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
568 + nd_log(NDLS_DAEMON, NDLP_ERR,
569 + "STREAM SEND[%zu] %s [to %s]: error during receive (%zd, on fd %d) - restarting connection.",
570 + sth->id, rrdhost_hostname(s->host), s->connected_to, rc, s->sock.fd);
571 + stream_sender_move_running_to_connector_or_remove(
572 + sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, true);
573 + return;
574 + }
575 }
576 }
559 -
560 - if(unlikely(s->rbuf.read_len)) {
561 - worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
562 - stream_sender_execute_commands(s);
563 - }
577 }
578
579 void stream_sender_cleanup(struct stream_thread *sth) {
580 // stop all hosts
581 Word_t idx = 0;
569 - for(struct sender_state *s = SENDERS_FIRST(&sth->snd.senders, &idx);
570 - s;
571 - s = SENDERS_NEXT(&sth->snd.senders, &idx)) {
582 + for(struct pollfd_meta *m = META_FIRST(&sth->run.meta, &idx);
583 + m;
584 + m = META_NEXT(&sth->run.meta, &idx)) {
585 + if(m->type != POLLFD_TYPE_SENDER) continue;
586 + struct sender_state *s = m->s;
587
588 ND_LOG_STACK lgs[] = {
589 ND_LOG_FIELD_STR(NDF_NIDL_NODE, s->host->hostname),
@@ -583,8 +598,4 @@ void stream_sender_cleanup(struct stream_thread *sth) {
598
599 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SHUTDOWN, false);
600 }
586 -
587 - // cleanup
588 - SENDERS_FREE(&sth->snd.senders, NULL);
601 }
590 -
src/streaming/stream-thread.c
+138 -34
@@ -4,7 +4,7 @@
4
5 struct stream_thread_globals stream_thread_globals = {
6 .assign = {
7 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
7 + .spinlock = SPINLOCK_INITIALIZER,
8 }
9 };
10
@@ -16,20 +16,35 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
16
17 sth->messages.processed++;
18
19 - struct sender_state *s = msg->sender ? SENDERS_GET(&sth->snd.senders, (Word_t)msg->sender) : NULL;
20 -
21 - if (msg->session && // there is a session
22 - s && // there is a sender
23 - (size_t)msg->thread_slot == sth->id) // same thread
19 + struct pollfd_meta *m = META_GET(&sth->run.meta, (Word_t)msg->meta);
20 + if (m && // there is a meta
21 + m == msg->meta && // the meta are equal
22 + msg->session && // there is a session
23 + (size_t)msg->thread_slot == sth->id && // the right thread
24 + (m->type == POLLFD_TYPE_SENDER || m->type == POLLFD_TYPE_RECEIVER) && // it is either sender or receiver
25 + ((m->type == POLLFD_TYPE_SENDER && m == &m->s->thread.meta) || // sender matches
26 + (m->type == POLLFD_TYPE_RECEIVER && m == &m->rpt->thread.meta))) // receiver matches
27 {
25 - if(msg->opcode & STREAM_OPCODE_SENDER_POLLOUT) {
26 - if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ|ND_POLL_WRITE, &s->thread.meta))
27 - internal_fatal(true, "Failed to update sender socket in nd_poll()");
28 - msg->opcode &= ~(STREAM_OPCODE_SENDER_POLLOUT);
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()");
32 + msg->opcode &= ~(STREAM_OPCODE_SENDER_POLLOUT);
33 + }
34 +
35 + if(msg->opcode)
36 + stream_sender_handle_op(sth, m->s, msg);
37 }
38 + else if(m->type == POLLFD_TYPE_RECEIVER) {
39 + 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()");
42 + msg->opcode &= ~(STREAM_OPCODE_RECEIVER_POLLOUT);
43 + }
44
31 - if(msg->opcode)
32 - stream_sender_handle_op(sth, s, msg);
45 + if (msg->opcode)
46 + stream_receiver_handle_op(sth, m->rpt, msg);
47 + }
48 }
49 else {
50 // this may happen if we receive a POLLOUT opcode, but the sender has been disconnected
@@ -37,36 +52,125 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
52 }
53 }
54
40 -void stream_sender_send_msg_to_dispatcher(struct sender_state *s, struct stream_opcode msg) {
41 - if (!msg.session || !msg.sender || !s)
55 +static void stream_thread_send_pipe_signal(struct stream_thread *sth) {
56 + if(sth->tid == gettid_cached())
57 + // no need for this if we are the same thread
58 + // we will process all the events shortly
59 return;
60
44 - internal_fatal(msg.sender != s, "the sender pointer in the message does not match this sender");
61 + if(sth->pipe.fds[PIPE_WRITE] != -1 &&
62 + write(sth->pipe.fds[PIPE_WRITE], " ", 1) != 1) {
63 + nd_log_limit_static_global_var(erl, 1, 1 * USEC_PER_MS);
64 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
65 + "STREAM THREAD[%zu]: cannot write to signal pipe", sth->id);
66 + }
67 +}
68 +
69 +void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcode msg) {
70 + if (!msg.session || !msg.meta || !rpt)
71 + return;
72
73 + internal_fatal(msg.meta != &rpt->thread.meta, "the receiver pointer in the message does not match this receiver");
74 struct stream_thread *sth = stream_thread_by_slot_id(msg.thread_slot);
75 if(!sth) {
76 internal_fatal(true,
49 - "STREAM SEND[x] [%s] thread pointer in the opcode message does not match the expected",
50 - rrdhost_hostname(s->host));
77 + "STREAM RECEIVE[x] [%s] thread pointer in the opcode message does not match the expected",
78 + rrdhost_hostname(rpt->host));
79 + return;
80 + }
81 +
82 + // check if we can execute the message now
83 + if(msg.opcode == STREAM_OPCODE_RECEIVER_POLLOUT && sth->tid == gettid_cached()) {
84 + // we are running at the stream thread, and the request is about enabling POLLOUT,
85 + // we can do this synchronously.
86 + // IMPORTANT: DO NOT HANDLE FAILURES THAT REMOVE THE RECEIVER OR THE SENDER THIS WAY
87 + // THE EVENT LOOP DRAINS THE INPUT SOCKET (BOTH RECEIVER AND SENDER)
88 + // AND THE LOOP WILL CRASH IF THE RECEIVER OR THE SENDER VANISH WHILE IT
89 + // WORKS WITH THEM!
90 + sth->messages.bypassed++;
91 + stream_thread_handle_op(sth, &msg);
92 return;
93 }
94
95 bool send_pipe_msg = false;
96
97 + // add it to the message queue of the thread
98 + spinlock_lock(&sth->messages.spinlock);
99 + {
100 + sth->messages.added++;
101 + if (rpt->thread.send_to_child.msg_slot >= sth->messages.used || sth->messages.array[rpt->thread.send_to_child.msg_slot].meta != &rpt->thread.meta) {
102 + if (unlikely(sth->messages.used >= sth->messages.size)) {
103 + // this should never happen, but let's find the root cause
104 +
105 + if (!sth->messages.size) {
106 + // we are exiting
107 + spinlock_unlock(&sth->messages.spinlock);
108 + return;
109 + }
110 +
111 + // try to find us in the list
112 + for (size_t i = 0; i < sth->messages.size; i++) {
113 + if (sth->messages.array[i].meta == &rpt->thread.meta) {
114 + rpt->thread.send_to_child.msg_slot = i;
115 + sth->messages.array[rpt->thread.send_to_child.msg_slot].opcode |= msg.opcode;
116 + spinlock_unlock(&sth->messages.spinlock);
117 + internal_fatal(true, "the stream opcode queue is full, but this receiver is already on slot %zu", i);
118 + return;
119 + }
120 + }
121 +
122 + fatal("The streaming opcode queue is full, but this should never happen");
123 + }
124 +
125 + // let's use a new slot
126 + send_pipe_msg = !sth->messages.used; // write to the pipe, only when the queue was empty before this msg
127 + rpt->thread.send_to_child.msg_slot = sth->messages.used++;
128 + sth->messages.array[rpt->thread.send_to_child.msg_slot] = msg;
129 + }
130 + else
131 + // the existing slot is good
132 + sth->messages.array[rpt->thread.send_to_child.msg_slot].opcode |= msg.opcode;
133 + }
134 + spinlock_unlock(&sth->messages.spinlock);
135 +
136 + // signal the streaming thread to wake up and process messages
137 + if(send_pipe_msg)
138 + stream_thread_send_pipe_signal(sth);
139 +}
140 +
141 +void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg) {
142 + if (!msg.session || !msg.meta || !s)
143 + return;
144 +
145 + internal_fatal(msg.meta != &s->thread.meta, "the sender pointer in the message does not match this sender");
146 + struct stream_thread *sth = stream_thread_by_slot_id(msg.thread_slot);
147 + 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));
151 + return;
152 + }
153 +
154 // check if we can execute the message now
57 - if(sth->tid == gettid_cached()) {
58 - // we are running at the dispatcher thread
59 - // no need for locks or queuing
155 + if(msg.opcode == STREAM_OPCODE_SENDER_POLLOUT && sth->tid == gettid_cached()) {
156 + // we are running at the stream thread, and the request is about enabling POLLOUT,
157 + // we can do this synchronously.
158 + // IMPORTANT: DO NOT HANDLE FAILURES THAT REMOVE THE RECEIVER OR THE SENDER THIS WAY
159 + // THE EVENT LOOP DRAINS THE INPUT SOCKET (BOTH RECEIVER AND SENDER)
160 + // AND THE LOOP WILL CRASH IF THE RECEIVER OR THE SENDER VANISH WHILE IT
161 + // WORKS WITH THEM!
162 sth->messages.bypassed++;
163 stream_thread_handle_op(sth, &msg);
164 return;
165 }
166
167 + bool send_pipe_msg = false;
168 +
169 // add it to the message queue of the thread
170 spinlock_lock(&sth->messages.spinlock);
171 {
172 sth->messages.added++;
69 - if (s->thread.msg_slot >= sth->messages.used || sth->messages.array[s->thread.msg_slot].sender != s) {
173 + if (s->thread.msg_slot >= sth->messages.used || sth->messages.array[s->thread.msg_slot].meta != &s->thread.meta) {
174 if (unlikely(sth->messages.used >= sth->messages.size)) {
175 // this should never happen, but let's find the root cause
176
@@ -78,7 +182,7 @@ void stream_sender_send_msg_to_dispatcher(struct sender_state *s, struct stream_
182
183 // try to find us in the list
184 for (size_t i = 0; i < sth->messages.size; i++) {
81 - if (sth->messages.array[i].sender == s) {
185 + if (sth->messages.array[i].meta == &s->thread.meta) {
186 s->thread.msg_slot = i;
187 sth->messages.array[s->thread.msg_slot].opcode |= msg.opcode;
188 spinlock_unlock(&sth->messages.spinlock);
@@ -87,7 +191,7 @@ void stream_sender_send_msg_to_dispatcher(struct sender_state *s, struct stream_
191 }
192 }
193
90 - fatal("the dispatcher message queue is full, but this should never happen");
194 + fatal("the streaming opcode queue is full, but this should never happen");
195 }
196
197 // let's use a new slot
@@ -102,14 +206,8 @@ void stream_sender_send_msg_to_dispatcher(struct sender_state *s, struct stream_
206 spinlock_unlock(&sth->messages.spinlock);
207
208 // signal the streaming thread to wake up and process messages
105 - if(send_pipe_msg &&
106 - sth->pipe.fds[PIPE_WRITE] != -1 &&
107 - write(sth->pipe.fds[PIPE_WRITE], " ", 1) != 1) {
108 - nd_log_limit_static_global_var(erl, 1, 1 * USEC_PER_MS);
109 - nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
110 - "STREAM SEND [%s]: cannot write to signal pipe",
111 - rrdhost_hostname(s->host));
112 - }
209 + if(send_pipe_msg)
210 + stream_thread_send_pipe_signal(sth);
211 }
212
213 static void stream_thread_read_pipe_messages(struct stream_thread *sth) {
@@ -184,7 +282,6 @@ static bool stream_thread_process_poll_slot(struct stream_thread *sth, nd_poll_r
282 switch(m->type) {
283 case POLLFD_TYPE_SENDER: {
284 struct sender_state *s = m->s;
187 - internal_fatal(SENDERS_GET(&sth->snd.senders, (Word_t)s) == NULL, "Sender is not found in the senders list");
285 stream_sender_process_poll_events(sth, s, ev->events, now_ut);
286 *replay_entries += dictionary_entries(s->replication.requests);
287 break;
@@ -192,7 +289,6 @@ static bool stream_thread_process_poll_slot(struct stream_thread *sth, nd_poll_r
289
290 case POLLFD_TYPE_RECEIVER: {
291 struct receiver_state *rpt = m->rpt;
195 - internal_fatal(RECEIVERS_GET(&sth->rcv.receivers, (Word_t)rpt) == NULL, "Receiver is not found in the receiver list");
292 stream_receive_process_poll_events(sth, rpt, ev->events, now_ut);
293 break;
294 }
@@ -250,7 +346,7 @@ void *stream_thread(void *ptr) {
346 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW, "disconnect overflow");
347 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT, "disconnect timeout");
348 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR, "disconnect socket error");
253 - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED, "disconnect parent closed");
349 + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED, "disconnect remote closed");
350 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR, "disconnect receive error");
351 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR, "disconnect send error");
352 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_COMPRESSION_ERROR, "disconnect compression error");
@@ -328,6 +424,8 @@ void *stream_thread(void *ptr) {
424 if(!sth->run.ndpl)
425 fatal("Cannot create nd_poll()");
426
427 + META_SET(&sth->run.meta, (Word_t)&sth->run.pipe, &sth->run.pipe);
428 +
429 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()");
431
@@ -393,6 +491,11 @@ void *stream_thread(void *ptr) {
491 if(nd_thread_signaled_to_cancel() || !service_running(SERVICE_STREAMING))
492 break;
493
494 + // nd_poll() may have received events for a socket we have already removed
495 + // so, if we don't find it in our meta index, do not access it - it has been removed
496 + if(META_GET(&sth->run.meta, (Word_t)ev.data) != ev.data)
497 + continue;
498 +
499 now_ut = now_monotonic_usec();
500 exit_thread = stream_thread_process_poll_slot(sth, &ev, now_ut, &replay_entries);
501 }
@@ -406,6 +509,7 @@ void *stream_thread(void *ptr) {
509 // cleanup receiver and dispatcher
510 stream_sender_cleanup(sth);
511 stream_receiver_cleanup(sth);
512 + META_FREE(&sth->run.meta, NULL);
513
514 // cleanup the thread structures
515 spinlock_lock(&sth->messages.spinlock);
src/streaming/stream-thread.h
+11 -8
@@ -4,6 +4,7 @@
4 #define NETDATA_STREAM_THREAD_H
5
6 #include "libnetdata/libnetdata.h"
7 +#include "stream-circular-buffer.h"
8
9 struct stream_thread;
10 struct pollfd_slotted {
@@ -17,17 +18,19 @@ struct pollfd_slotted {
18 typedef enum __attribute__((packed)) {
19 STREAM_OPCODE_NONE = 0,
20 STREAM_OPCODE_SENDER_POLLOUT = (1 << 0), // move traffic around as soon as possible
20 - STREAM_OPCODE_SENDER_BUFFER_OVERFLOW = (1 << 1), // reconnect the node, it has buffer overflow
21 - STREAM_OPCODE_SENDER_RECONNECT_WITHOUT_COMPRESSION = (1 << 2), // reconnect the node, but disable compression
22 - STREAM_OPCODE_SENDER_STOP_RECEIVER_LEFT = (1 << 3), // disconnect the node, the receiver left
23 - STREAM_OPCODE_SENDER_STOP_HOST_CLEANUP = (1 << 4), // disconnect the node, it is being de-allocated
21 + STREAM_OPCODE_RECEIVER_POLLOUT = (1 << 1), // disconnect the node, it has buffer overflow
22 + STREAM_OPCODE_SENDER_BUFFER_OVERFLOW = (1 << 2), // reconnect the node, it has buffer overflow
23 + STREAM_OPCODE_RECEIVER_BUFFER_OVERFLOW = (1 << 3), // reconnect the node, it has buffer overflow
24 + STREAM_OPCODE_SENDER_RECONNECT_WITHOUT_COMPRESSION = (1 << 4), // reconnect the node, but disable compression
25 + STREAM_OPCODE_SENDER_STOP_RECEIVER_LEFT = (1 << 5), // disconnect the node, the receiver left
26 + STREAM_OPCODE_SENDER_STOP_HOST_CLEANUP = (1 << 6), // disconnect the node, it is being de-allocated
27 } STREAM_OPCODE;
28
29 struct stream_opcode {
30 int32_t thread_slot; // the dispatcher id this message refers to
31 uint32_t session; // random number used to verify that the message the dispatcher receives is for this sender
32 STREAM_OPCODE opcode; // the actual message to be delivered
30 - struct sender_state *sender;
33 + struct pollfd_meta *meta;
34 };
35
36 // IMPORTANT: to add workers, you have to edit WORKER_PARSER_FIRST_JOB accordingly
@@ -61,7 +64,7 @@ struct stream_opcode {
64 #define WORKER_SENDER_JOB_DISCONNECT_OVERFLOW (WORKER_PARSER_FIRST_JOB - 18)
65 #define WORKER_SENDER_JOB_DISCONNECT_TIMEOUT (WORKER_PARSER_FIRST_JOB - 17)
66 #define WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR (WORKER_PARSER_FIRST_JOB - 16)
64 -#define WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED (WORKER_PARSER_FIRST_JOB - 15)
67 +#define WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED (WORKER_PARSER_FIRST_JOB - 15)
68 #define WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR (WORKER_PARSER_FIRST_JOB - 14)
69 #define WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR (WORKER_PARSER_FIRST_JOB - 13)
70 #define WORKER_SENDER_JOB_DISCONNECT_COMPRESSION_ERROR (WORKER_PARSER_FIRST_JOB - 12)
@@ -105,6 +108,7 @@ struct pollfd_meta {
108
109 DEFINE_JUDYL_TYPED(SENDERS, struct sender_state *);
110 DEFINE_JUDYL_TYPED(RECEIVERS, struct receiver_state *);
111 +DEFINE_JUDYL_TYPED(META, struct pollfd_meta *);
112
113 struct stream_thread {
114 ND_THREAD *thread;
@@ -114,13 +118,11 @@ struct stream_thread {
118 size_t nodes_count;
119
120 struct {
117 - SENDERS_JudyLSet senders;
121 size_t bytes_received;
122 size_t bytes_sent;
123 } snd;
124
125 struct {
123 - RECEIVERS_JudyLSet receivers;
126 size_t bytes_received;
127 size_t bytes_received_uncompressed;
128 NETDATA_DOUBLE replication_completion;
@@ -155,6 +157,7 @@ struct stream_thread {
157 struct {
158 nd_poll_t *ndpl;
159 struct pollfd_meta pipe;
160 + META_JudyLSet meta;
161 } run;
162 };
163
src/streaming/stream.h
-1
@@ -38,7 +38,6 @@ void stream_receiver_free(struct receiver_state *rpt);
38 bool stream_receiver_signal_to_stop_and_wait(struct rrdhost *host, STREAM_HANDSHAKE reason);
39 char *stream_receiver_program_version_strdupz(struct rrdhost *host);
40
41 -#include "replication.h"
41 #include "rrdhost-status.h"
42 #include "protocol/commands.h"
43 #include "stream-path.h"
src/web/api/queries/query.c
+2 -2
@@ -2018,7 +2018,7 @@ void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s
2018
2019 storage_engine_query_finalize(&seqh);
2020 store_metric_collection_completed();
2021 - telemetry_queries_backfill_query_completed(points_read);
2021 + pulse_queries_backfill_query_completed(points_read);
2022
2023 //internal_error(true, "DBENGINE: backfilled chart '%s', dimension '%s', tier %d, from %ld to %ld, with %zu points from tier %d",
2024 // rd->rrdset->name, rd->name, tier, after_wanted, before_wanted, points, tr);
@@ -3592,7 +3592,7 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3592 continue;
3593 }
3594
3595 - telemetry_queries_rrdr_query_completed(
3595 + pulse_queries_rrdr_query_completed(
3596 1,
3597 r_tmp->stats.db_points_read - last_db_points_read,
3598 r_tmp->stats.result_points_generated - last_result_points_generated,
src/web/api/v3/api_v3_settings.c
+1 -1
@@ -37,7 +37,7 @@
37
38 // we need an r/w spinlock to ensure that reads and write do not happen
39 // concurrently for settings files
40 -static RW_SPINLOCK settings_spinlock = NETDATA_RW_SPINLOCK_INITIALIZER;
40 +static RW_SPINLOCK settings_spinlock = RW_SPINLOCK_INITIALIZER;
41
42 static inline void settings_path(char out[FILENAME_MAX]) {
43 filename_from_path_entry(out, netdata_configured_varlib_dir, "settings", NULL);
src/web/rtc/webrtc.c
+1 -1
@@ -115,7 +115,7 @@ static struct {
115 .proxyServer = NULL, // [("http"|"socks5") (":"|"://")][username ":" password "@"]hostname[" :" port]
116 .bindAddress = NULL,
117 .unsafe = {
118 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
118 + .spinlock = SPINLOCK_INITIALIZER,
119 .head = NULL,
120 },
121 };
src/web/server/static/static-threaded.c
+4 -3
@@ -123,7 +123,7 @@ static void web_server_file_del_callback(POLLINFO *pi) {
123 web_server_log_connection(w, "DISCONNECTED");
124 web_client_request_done(w);
125 web_client_release_to_cache(w);
126 - telemetry_web_client_disconnected();
126 + pulse_web_client_disconnected();
127 }
128
129 worker_is_idle();
@@ -269,7 +269,7 @@ static void web_server_del_callback(POLLINFO *pi) {
269 web_server_log_connection(w, "DISCONNECTED");
270 web_client_request_done(w);
271 web_client_release_to_cache(w);
272 - telemetry_web_client_disconnected();
272 + pulse_web_client_disconnected();
273 }
274
275 worker_is_idle();
@@ -509,7 +509,8 @@ void *socket_listen_main_static_threaded(void *ptr) {
509 // 6 threads is the optimal value
510 // since 6 are the parallel connections browsers will do
511 // so, if the machine has more CPUs, avoid using resources unnecessarily
512 - int def_thread_count = MIN(get_netdata_cpus(), 6);
512 + int def_thread_count = (int)get_netdata_cpus();
513 + if(def_thread_count < 6) def_thread_count = 6;
514
515 if (!strcmp(config_get(CONFIG_SECTION_WEB, "mode", ""),"single-threaded")) {
516 netdata_log_info("Running web server with one thread, because mode is single-threaded");
src/web/server/web_client.c
+1 -1
@@ -225,7 +225,7 @@ void web_client_log_completed_request(struct web_client *w, bool update_web_stat
225 size_t sent = w->response.zoutput ? (size_t)w->response.zstream.total_out : size;
226
227 if(update_web_stats)
228 - telemetry_web_request_completed(
228 + pulse_web_request_completed(
229 dt_usec(&tv, &w->timings.tv_in), w->statistics.received_bytes, w->statistics.sent_bytes, size, sent);
230
231 usec_t prep_ut = w->timings.tv_ready.tv_sec ? dt_usec(&w->timings.tv_ready, &w->timings.tv_in) : 0;
src/web/server/web_client_cache.c
+3 -3
@@ -33,14 +33,14 @@ static struct clients_cache {
33 } avail;
34 } web_clients_cache = {
35 .used = {
36 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
36 + .spinlock = SPINLOCK_INITIALIZER,
37 .head = NULL,
38 .count = 0,
39 .reused = 0,
40 .allocated = 0,
41 },
42 .avail = {
43 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
43 + .spinlock = SPINLOCK_INITIALIZER,
44 .head = NULL,
45 .count = 0,
46 },
@@ -103,7 +103,7 @@ struct web_client *web_client_get_from_cache(void) {
103 w = web_client_create(&netdata_buffers_statistics.buffers_web);
104 spinlock_lock(&web_clients_cache.used.spinlock);
105
106 - w->id = telemetry_web_client_connected();
106 + w->id = pulse_web_client_connected();
107 web_clients_cache.used.allocated++;
108 }
109