Streaming Improvements No 5 (#19193)
* rrdhost state id is now used to detect not available functions * acquire release for rrdhost state * initialize rddhost state for local hosts * track send misses * log for functions that return 503 * fix rrd_collector_finished() call from stream threads
Costa Tsaousis committed
Dec 12, 2024 at 18:20 UTC
bf4a3a55f08f2df3de98fd531b50386f2e9ca63a
16 files changed
+182
-32
CMakeLists.txt
+2
@@ -1431,6 +1431,8 @@ set(RRD_PLUGIN_FILES
1431
src/database/rrdcollector-internals.h
1432
src/database/rrd-database-mode.h
1433
src/database/rrd-database-mode.c
1434
+ src/database/rrdhost-state-id.c
1435
+ src/database/rrdhost-state-id.h
1436
)
1437
1438
if(ENABLE_DBENGINE)
src/collectors/ebpf.plugin/ebpf_socket.c
-1
@@ -1759,7 +1759,6 @@ end_socket_loop: ; // the empty statement is here to allow code to be compiled b
1759
else {
1760
ebpf_release_pid_data(local_pid, fd, key.pid, EBPF_MODULE_SOCKET_IDX);
1761
ebpf_socket_release_publish(curr);
1762
- local_pid->socket = NULL;
1762
}
1763
memset(values, 0, length);
1764
memcpy(&key, &next_key, sizeof(key));
src/database/rrd.h
+6
-1
@@ -11,6 +11,7 @@ extern "C" {
11
#include "rrd-database-mode.h"
12
#include "streaming/stream-traffic-types.h"
13
#include "streaming/stream-sender-commit.h"
14
+#include "rrdhost-state-id.h"
15
16
// non-existing structs instead of voids
17
// to enable type checking at compile time
@@ -1161,6 +1162,11 @@ struct rrdhost {
1162
STRING *program_name; // the program name that collects metrics for this host
1163
STRING *program_version; // the program version that collects metrics for this host
1164
1165
+ REFCOUNT state_refcount;
1166
+ RRDHOST_STATE state_id; // every time data collection (stream receiver) (dis)connects,
1167
+ // this gets incremented - it is used to detect stale functions,
1168
+ // stale backfilling requests, etc.
1169
+
1170
int32_t utc_offset; // the offset in seconds from utc
1171
1172
RRDHOST_OPTIONS options; // configuration option for this RRDHOST (no atomics on this)
@@ -1238,7 +1244,6 @@ struct rrdhost {
1244
1245
struct {
1246
pid_t tid;
1241
- uint32_t state_id; // every time the receiver connects/disconnects, this is incremented
1247
1248
time_t last_connected; // the time the last sender was connected
1249
time_t last_disconnected; // the time the last sender was disconnected
src/database/rrdfunctions-internals.h
+1
@@ -29,6 +29,7 @@ struct rrd_host_function {
29
rrd_function_execute_cb_t execute_cb;
30
void *execute_cb_data;
31
32
+ RRDHOST_STATE rrdhost_state_id;
33
struct rrd_collector *collector;
34
};
35
src/database/rrdfunctions.c
+30
-3
@@ -15,11 +15,12 @@
15
// ----------------------------------------------------------------------------
16
17
static void rrd_functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, void *rrdhost) {
18
- RRDHOST *host = rrdhost; (void)host;
18
+ RRDHOST *host = rrdhost;
19
struct rrd_host_function *rdcf = func;
20
21
rrd_collector_started();
22
rdcf->collector = rrd_collector_acquire_current_thread();
23
+ rdcf->rrdhost_state_id = rrdhost_state_id(host);
24
25
if(!rdcf->priority)
26
rdcf->priority = RRDFUNCTIONS_PRIORITY_DEFAULT;
@@ -57,6 +58,17 @@ static bool rrd_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_
58
changed = true;
59
}
60
61
+ if(rdcf->rrdhost_state_id != rrdhost_state_id(host)) {
62
+ nd_log(NDLS_DAEMON, NDLP_DEBUG,
63
+ "FUNCTIONS: function '%s' of host '%s' changed state id from %u to %u",
64
+ dictionary_acquired_item_name(item), rrdhost_hostname(host),
65
+ rdcf->rrdhost_state_id,
66
+ rrdhost_state_id(host));
67
+
68
+ rdcf->rrdhost_state_id = rrdhost_state_id(host);
69
+ changed = true;
70
+ }
71
+
72
if(rdcf->execute_cb != new_rdcf->execute_cb) {
73
nd_log(NDLS_DAEMON, NDLP_DEBUG,
74
"FUNCTIONS: function '%s' of host '%s' changed execute callback",
@@ -260,6 +272,8 @@ int rrd_functions_find_by_name(RRDHOST *host, BUFFER *wb, const char *name, size
272
strncpyz(buffer, name, sizeof(buffer) - 1);
273
char *s = NULL;
274
275
+ RRDHOST_STATE state_id = rrdhost_state_id(host);
276
+
277
bool found = false;
278
*item = NULL;
279
if(host->functions) {
@@ -268,10 +282,23 @@ int rrd_functions_find_by_name(RRDHOST *host, BUFFER *wb, const char *name, size
282
found = true;
283
284
struct rrd_host_function *rdcf = dictionary_acquired_item_value(*item);
271
- if(rrd_collector_running(rdcf->collector)) {
285
+ if(rrd_collector_running(rdcf->collector) && rdcf->rrdhost_state_id == state_id) {
286
break;
287
}
288
else {
289
+
290
+ nd_log(NDLS_DAEMON, NDLP_DEBUG,
291
+ "Function '%s' is not available. "
292
+ "host '%s', collector = { tid: %d, running: %s }, host tid { rcv: %d, snd: %d }, host state { id: %u, expected %u }, hops: %d",
293
+ name,
294
+ rrdhost_hostname(host),
295
+ rrd_collector_tid(rdcf->collector),
296
+ rrd_collector_running(rdcf->collector) ? "yes" : "no",
297
+ host->stream.rcv.status.tid, host->stream.snd.status.tid,
298
+ state_id, rdcf->rrdhost_state_id,
299
+ host->system_info->hops
300
+ );
301
+
302
dictionary_acquired_item_release(host->functions, *item);
303
*item = NULL;
304
}
@@ -314,7 +341,7 @@ bool rrd_function_available(RRDHOST *host, const char *function) {
341
const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(host->functions, function);
342
if(item) {
343
struct rrd_host_function *rdcf = dictionary_acquired_item_value(item);
317
- if(rrd_collector_running(rdcf->collector))
344
+ if(rrd_collector_running(rdcf->collector) && rdcf->rrdhost_state_id == rrdhost_state_id(host))
345
ret = true;
346
347
dictionary_acquired_item_release(host->functions, item);
src/database/rrdhost-state-id.c
new
+73
@@ -0,0 +1,73 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "rrdhost-state-id.h"
4
+#include "rrd.h"
5
+
6
+RRDHOST_STATE rrdhost_state_id(struct rrdhost *host) {
7
+ return __atomic_load_n(&host->state_id, __ATOMIC_RELAXED);
8
+}
9
+
10
+bool rrdhost_state_connected(RRDHOST *host) {
11
+ __atomic_add_fetch(&host->state_id, 1, __ATOMIC_RELAXED);
12
+
13
+ int32_t expected = __atomic_load_n(&host->state_refcount, __ATOMIC_RELAXED);
14
+ int32_t desired;
15
+
16
+ do {
17
+ if(expected >= 0) {
18
+ internal_fatal(true, "Cannot get the node connected");
19
+ return false;
20
+ }
21
+
22
+ desired = 0;
23
+
24
+ } while(!__atomic_compare_exchange_n(
25
+ &host->state_refcount, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
26
+
27
+ return true;
28
+}
29
+
30
+bool rrdhost_state_disconnected(RRDHOST *host) {
31
+ __atomic_add_fetch(&host->state_id, 1, __ATOMIC_RELAXED);
32
+
33
+ int32_t expected = __atomic_load_n(&host->state_refcount, __ATOMIC_RELAXED);
34
+ int32_t desired;
35
+
36
+ do {
37
+ if(expected < 0) {
38
+ internal_fatal(true, "Cannot get the node disconnected");
39
+ return false;
40
+ }
41
+
42
+ desired = -1;
43
+
44
+ } while(!__atomic_compare_exchange_n(
45
+ &host->state_refcount, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
46
+
47
+ return true;
48
+}
49
+
50
+bool rrdhost_state_acquire(RRDHOST *host, RRDHOST_STATE wanted_state_id) {
51
+ int32_t expected = __atomic_load_n(&host->state_refcount, __ATOMIC_RELAXED);
52
+ int32_t desired;
53
+
54
+ do {
55
+ if(expected < 0)
56
+ return false;
57
+
58
+ desired = expected + 1;
59
+
60
+ } while(!__atomic_compare_exchange_n(
61
+ &host->state_refcount, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
62
+
63
+ if(rrdhost_state_id(host) != wanted_state_id) {
64
+ rrdhost_state_release(host);
65
+ return false;
66
+ }
67
+
68
+ return true;
69
+}
70
+
71
+void rrdhost_state_release(RRDHOST *host) {
72
+ __atomic_sub_fetch(&host->state_refcount, 1, __ATOMIC_RELAXED);
73
+}
src/database/rrdhost-state-id.h
new
+19
@@ -0,0 +1,19 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#ifndef NETDATA_RRDHOST_STATE_ID_H
4
+#define NETDATA_RRDHOST_STATE_ID_H
5
+
6
+#include "libnetdata/libnetdata.h"
7
+
8
+typedef uint32_t RRDHOST_STATE;
9
+
10
+struct rrdhost;
11
+RRDHOST_STATE rrdhost_state_id(struct rrdhost *host);
12
+
13
+bool rrdhost_state_connected(struct rrdhost *host);
14
+bool rrdhost_state_disconnected(struct rrdhost *host);
15
+
16
+bool rrdhost_state_acquire(struct rrdhost *host, RRDHOST_STATE wanted_state_id);
17
+void rrdhost_state_release(struct rrdhost *host);
18
+
19
+#endif //NETDATA_RRDHOST_STATE_ID_H
src/database/rrdhost.c
+3
@@ -327,6 +327,8 @@ static RRDHOST *rrdhost_create(
327
}
328
329
RRDHOST *host = callocz(1, sizeof(RRDHOST));
330
+ host->state_refcount = -1;
331
+
332
__atomic_add_fetch(&netdata_buffers_statistics.rrdhost_allocations_size, sizeof(RRDHOST), __ATOMIC_RELAXED);
333
334
strncpyz(host->machine_guid, guid, GUID_LEN + 1);
@@ -840,6 +842,7 @@ int rrd_init(const char *hostname, struct rrdhost_system_info *system_info, bool
842
return 1;
843
844
rrdhost_flag_set(localhost, RRDHOST_FLAG_COLLECTOR_ONLINE);
845
+ rrdhost_state_connected(localhost);
846
847
ml_host_start(localhost);
848
dyncfg_host_init(localhost);
src/libnetdata/common.h
+4
@@ -400,6 +400,10 @@ typedef uint32_t uid_t;
400
401
// --------------------------------------------------------------------------------------------------------------------
402
403
+typedef int32_t REFCOUNT;
404
+
405
+// --------------------------------------------------------------------------------------------------------------------
406
+
407
#if defined(OS_WINDOWS)
408
#include <windows.h>
409
#include <wctype.h>
src/plugins.d/pluginsd_parser.c
+15
-12
@@ -205,6 +205,7 @@ static inline PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, si
205
206
rrdhost_option_set(host, RRDHOST_OPTION_VIRTUAL_HOST);
207
rrdhost_flag_set(host, RRDHOST_FLAG_COLLECTOR_ONLINE);
208
+ rrdhost_state_connected(host);
209
ml_host_start(host);
210
dyncfg_host_init(host);
211
@@ -376,16 +377,19 @@ static inline PARSER_RC pluginsd_chart(char **words, size_t num_words, PARSER *p
377
}
378
379
static void backfill_callback(size_t successful_dims __maybe_unused, size_t failed_dims __maybe_unused, struct backfill_request_data *brd) {
379
- if (brd->rrdhost_receiver_state_id == __atomic_load_n(&brd->host->stream.rcv.status.state_id, __ATOMIC_RELAXED)) {
380
- if (!replicate_chart_request(send_to_plugin, brd->parser, brd->host, brd->st,
381
- brd->first_entry_child, brd->last_entry_child, brd->child_wall_clock_time,
382
- 0, 0)) {
383
- netdata_log_error(
384
- "PLUGINSD: 'host:%s' failed to initiate replication for 'chart:%s'",
385
- rrdhost_hostname(brd->host),
386
- rrdset_id(brd->st));
387
- }
380
+ if(!rrdhost_state_acquire(brd->host, brd->rrdhost_receiver_state_id))
381
+ return;
382
+
383
+ if (!replicate_chart_request(send_to_plugin, brd->parser, brd->host, brd->st,
384
+ brd->first_entry_child, brd->last_entry_child, brd->child_wall_clock_time,
385
+ 0, 0)) {
386
+ netdata_log_error(
387
+ "PLUGINSD: 'host:%s' failed to initiate replication for 'chart:%s'",
388
+ rrdhost_hostname(brd->host),
389
+ rrdset_id(brd->st));
390
}
391
+
392
+ rrdhost_state_release(brd->host);
393
}
394
395
static inline PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, PARSER *parser) {
@@ -417,7 +421,7 @@ static inline PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_w
421
rrdhost_receiver_replicating_charts_plus_one(st->rrdhost);
422
423
struct backfill_request_data brd = {
420
- .rrdhost_receiver_state_id =__atomic_load_n(&host->stream.rcv.status.state_id, __ATOMIC_RELAXED),
424
+ .rrdhost_receiver_state_id = rrdhost_state_id(host),
425
.parser = parser,
426
.host = host,
427
.st = st,
@@ -1173,8 +1177,6 @@ void pluginsd_process_cleanup(PARSER *parser) {
1177
pluginsd_cleanup_v2(parser);
1178
pluginsd_host_define_cleanup(parser);
1179
1176
- rrd_collector_finished();
1177
-
1180
#ifdef NETDATA_LOG_STREAM_RECEIVE
1181
if(parser->user.stream_log_fp) {
1182
fclose(parser->user.stream_log_fp);
@@ -1188,6 +1190,7 @@ void pluginsd_process_cleanup(PARSER *parser) {
1190
void pluginsd_process_thread_cleanup(void *pptr) {
1191
PARSER *parser = CLEANUP_FUNCTION_GET_PTR(pptr);
1192
pluginsd_process_cleanup(parser);
1193
+ rrd_collector_finished();
1194
}
1195
1196
bool parser_reconstruct_node(BUFFER *wb, void *ptr) {
src/plugins.d/pluginsd_parser.h
+1
-1
@@ -5,7 +5,7 @@
5
6
#include "daemon/common.h"
7
8
-#define WORKER_PARSER_FIRST_JOB 35
8
+#define WORKER_PARSER_FIRST_JOB 36
9
10
// this has to be in-sync with the same at stream-thread.c
11
#define WORKER_RECEIVER_JOB_REPLICATION_COMPLETION 25
src/streaming/stream-receiver.c
+4
-5
@@ -358,8 +358,6 @@ static void streaming_parser_init(struct receiver_state *rpt) {
358
359
pluginsd_keywords_init(parser, PARSER_INIT_STREAMING);
360
361
- rrd_collector_started();
362
-
361
rpt->thread.compressed.start = 0;
362
rpt->thread.compressed.used = 0;
363
rpt->thread.compressed.enabled = stream_decompression_initialize(rpt);
@@ -446,6 +444,7 @@ void stream_receiver_move_to_running_unsafe(struct stream_thread *sth, struct re
444
sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
445
446
stream_receive_log_database_gap(rpt);
447
+ rrdhost_state_connected(rpt->host);
448
449
// keep this last, since it sends commands back to the child
450
streaming_parser_init(rpt);
@@ -477,6 +476,8 @@ static void stream_receiver_remove(struct stream_thread *sth, struct receiver_st
476
, rpt->client_port ? rpt->client_port : "-"
477
, why ? why : "");
478
479
+ rrdhost_state_disconnected(rpt->host);
480
+
481
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");
482
META_DEL(&sth->run.meta, (Word_t)&rpt->thread.meta);
483
@@ -806,8 +807,6 @@ bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
807
rrdhost_receiver_lock(host);
808
809
if (!host->receiver) {
809
- __atomic_add_fetch(&host->stream.rcv.status.state_id, 1, __ATOMIC_RELAXED);
810
-
810
rrdhost_flag_clear(host, RRDHOST_FLAG_ORPHAN);
811
812
host->stream.rcv.status.connections++;
@@ -870,8 +869,8 @@ void rrdhost_clear_receiver(struct receiver_state *rpt) {
869
// Make sure that we detach this thread and don't kill a freshly arriving receiver
870
871
if (host->receiver == rpt) {
873
- __atomic_add_fetch(&host->stream.rcv.status.state_id, 1, __ATOMIC_RELAXED);
872
rrdhost_flag_clear(host, RRDHOST_FLAG_COLLECTOR_ONLINE);
873
+
874
rrdhost_receiver_unlock(host);
875
{
876
// run all these without having the receiver lock
src/streaming/stream-sender.c
+3
-1
@@ -540,8 +540,10 @@ bool stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
540
return false;
541
}
542
}
543
- else
543
+ else {
544
+ sth->snd.send_misses++;
545
break;
546
+ }
547
}
548
}
549
src/streaming/stream-thread.c
+12
-2
@@ -450,10 +450,15 @@ void *stream_thread(void *ptr) {
450
"ops processed", "messages",
451
WORKER_METRIC_INCREMENTAL_TOTAL);
452
453
- worker_register_job_custom_metric(WORKER_SENDER_JOB_RECEIVERS_WAITING_LIST_SIZE,
453
+ worker_register_job_custom_metric(WORKER_STREAM_JOB_RECEIVERS_WAITING_LIST_SIZE,
454
"receivers waiting to be added", "nodes",
455
WORKER_METRIC_ABSOLUTE);
456
457
+ worker_register_job_custom_metric(WORKER_STREAM_JOB_SEND_MISSES,
458
+ "send misses", "misses",
459
+ WORKER_METRIC_INCREMENTAL_TOTAL);
460
+
461
+
462
if(pipe(sth->pipe.fds) != 0) {
463
nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu]: cannot create required pipe.", sth->id);
464
sth->pipe.fds[PIPE_READ] = -1;
@@ -487,6 +492,8 @@ void *stream_thread(void *ptr) {
492
sth->snd.bytes_received = 0;
493
sth->snd.bytes_sent = 0;
494
495
+ rrd_collector_started();
496
+
497
while(!exit_thread && !nd_thread_signaled_to_cancel() && service_running(SERVICE_STREAMING)) {
498
usec_t now_ut = now_monotonic_usec();
499
@@ -521,7 +528,8 @@ void *stream_thread(void *ptr) {
528
worker_set_metric(WORKER_SENDER_JOB_BYTES_SENT, (NETDATA_DOUBLE)sth->snd.bytes_sent);
529
worker_set_metric(WORKER_SENDER_JOB_REPLAY_DICT_SIZE, (NETDATA_DOUBLE)replay_entries);
530
524
- worker_set_metric(WORKER_SENDER_JOB_RECEIVERS_WAITING_LIST_SIZE, (NETDATA_DOUBLE)receivers_waiting);
531
+ worker_set_metric(WORKER_STREAM_JOB_RECEIVERS_WAITING_LIST_SIZE, (NETDATA_DOUBLE)receivers_waiting);
532
+ worker_set_metric(WORKER_STREAM_JOB_SEND_MISSES, (NETDATA_DOUBLE)sth->snd.send_misses);
533
replay_entries = 0;
534
sth->snd.bytes_received = 0;
535
sth->snd.bytes_sent = 0;
@@ -597,6 +605,8 @@ void *stream_thread(void *ptr) {
605
606
worker_unregister();
607
608
+ rrd_collector_finished();
609
+
610
return NULL;
611
}
612
src/streaming/stream-thread.h
+3
-1
@@ -83,7 +83,8 @@ struct stream_opcode {
83
#define WORKER_SENDER_JOB_BYTES_COMPRESSION_RATIO 32
84
#define WORKER_SENDER_JOB_REPLAY_DICT_SIZE 33
85
#define WORKER_SENDER_JOB_MESSAGES 34
86
-#define WORKER_SENDER_JOB_RECEIVERS_WAITING_LIST_SIZE 35
86
+#define WORKER_STREAM_JOB_RECEIVERS_WAITING_LIST_SIZE 35
87
+#define WORKER_STREAM_JOB_SEND_MISSES 36
88
89
// IMPORTANT: to add workers, you have to edit WORKER_PARSER_FIRST_JOB accordingly
90
@@ -125,6 +126,7 @@ struct stream_thread {
126
struct {
127
size_t bytes_received;
128
size_t bytes_sent;
129
+ size_t send_misses;
130
} snd;
131
132
struct {
src/web/api/queries/backfill.c
+6
-5
@@ -48,7 +48,7 @@ bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_re
48
if(backfill_globals.running) {
49
struct backfill_request *br = aral_mallocz(backfill_globals.ar_br);
50
br->data = *data;
51
- br->rrdhost_receiver_state_id =__atomic_load_n(&st->rrdhost->stream.rcv.status.state_id, __ATOMIC_RELAXED);
51
+ br->rrdhost_receiver_state_id = rrdhost_state_id(st->rrdhost);
52
br->rsa = rrdset_find_and_acquire(st->rrdhost, string2str(st->id));
53
if(br->rsa) {
54
br->cb = cb;
@@ -95,19 +95,20 @@ bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_re
95
bool backfill_execute(struct backfill_dim_work *bdm) {
96
RRDSET *st = rrdset_acquired_to_rrdset(bdm->br->rsa);
97
98
- if(bdm->br->rrdhost_receiver_state_id !=__atomic_load_n(&st->rrdhost->stream.rcv.status.state_id, __ATOMIC_RELAXED))
98
+ if(!rrdhost_state_acquire(st->rrdhost, bdm->br->rrdhost_receiver_state_id))
99
return false;
100
101
+ size_t success = 0;
102
RRDDIM *rd = rrddim_acquired_to_rrddim(bdm->rda);
103
103
- size_t success = 0;
104
for (size_t tier = 1; tier < storage_tiers; tier++)
105
- if(backfill_tier_from_smaller_tiers(rd, tier, now_realtime_sec()))
105
+ if (backfill_tier_from_smaller_tiers(rd, tier, now_realtime_sec()))
106
success++;
107
108
- if(success > 0)
108
+ if (success > 0)
109
rrddim_option_set(rd, RRDDIM_OPTION_BACKFILLED_HIGH_TIERS);
110
111
+ rrdhost_state_release(st->rrdhost);
112
return success > 0;
113
}
114