Improve agent shutdown (#20280)
* Join web threads on shutdown Join cgroups discover thread No capabilities on LWT Free statsd structure / join threads Signal more services on shutdown Exporting engine free instance Join statsd collection threads * Message doing shutdown for pending threads to join * Add missing lock * Do not join main WEB thread * Attempt to join only exited threads * Switch to sleep_usec * Make sure to finalize statements before setting thread exit status * Revert some of the signalling for now Fix condition for CTXLOAD thread creation failure
Stelios Fragkakis committed
May 16, 2025 at 10:33 UTC
71e08dba6bfcdb478aaf5872a7b340ec09524f0d
9 files changed
+55
-30
src/aclk/aclk_tx_msgs.c
+1
-1
@@ -204,7 +204,7 @@ uint16_t aclk_send_agent_connection_update(mqtt_wss_client client, int reachable
204
.reachable = (reachable ? 1 : 0),
205
.lwt = 0,
206
.session_id = aclk_session_newarch,
207
- .capabilities = aclk_get_agent_capas()
207
+ .capabilities = aclk_get_agent_capas(),
208
};
209
210
CLAIM_ID claim_id = claim_id_get();
src/collectors/cgroups.plugin/sys_fs_cgroup.c
+3
@@ -1342,6 +1342,9 @@ static void cgroup_main_cleanup(void *pptr) {
1342
sleep_usec(step);
1343
}
1344
}
1345
+ // We should be done, but just in case, avoid blocking shutdown
1346
+ if (__atomic_load_n(&discovery_thread.exited, __ATOMIC_RELAXED))
1347
+ (void) nd_thread_join(discovery_thread.thread);
1348
1349
static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
1350
}
src/collectors/statsd.plugin/statsd.c
+14
-16
@@ -234,7 +234,7 @@ typedef struct statsd_app {
234
235
struct collection_thread_status {
236
SPINLOCK spinlock;
237
- bool running;
237
+ bool initializing;
238
uint32_t max_sockets;
239
240
ND_THREAD *thread;
@@ -1083,10 +1083,6 @@ void statsd_collector_thread_cleanup(void *pptr) {
1083
struct statsd_udp *d = CLEANUP_FUNCTION_GET_PTR(pptr);
1084
if(!d) return;
1085
1086
- spinlock_lock(&d->status->spinlock);
1087
- d->status->running = false;
1088
- spinlock_unlock(&d->status->spinlock);
1089
-
1086
#ifdef HAVE_RECVMMSG
1087
size_t i;
1088
for (i = 0; i < d->size; i++)
@@ -1107,7 +1103,7 @@ static bool statsd_should_stop(void) {
1103
void *statsd_collector_thread(void *ptr) {
1104
struct collection_thread_status *status = ptr;
1105
spinlock_lock(&status->spinlock);
1110
- status->running = true;
1106
+ status->initializing = false;
1107
spinlock_unlock(&status->spinlock);
1108
1109
worker_register("STATSD");
@@ -2402,17 +2398,18 @@ static void statsd_main_cleanup(void *pptr) {
2398
if (statsd.collection_threads_status) {
2399
int i;
2400
for (i = 0; i < statsd.threads; i++) {
2405
- spinlock_lock(&statsd.collection_threads_status[i].spinlock);
2406
-
2407
- if(statsd.collection_threads_status[i].running) {
2408
- collector_info("STATSD: signalling data collection thread %d to stop...", i + 1);
2409
- nd_thread_signal_cancel(statsd.collection_threads_status[i].thread);
2410
- }
2411
- else
2412
- collector_info("STATSD: data collection thread %d found stopped.", i + 1);
2413
-
2414
- spinlock_unlock(&statsd.collection_threads_status[i].spinlock);
2401
+ bool initializing;
2402
+ do {
2403
+ spinlock_lock(&statsd.collection_threads_status[i].spinlock);
2404
+ initializing = statsd.collection_threads_status[i].initializing;
2405
+ spinlock_unlock(&statsd.collection_threads_status[i].spinlock);
2406
+ if (unlikely(initializing))
2407
+ sleep_usec(1000);
2408
+ } while(initializing);
2409
+
2410
+ (void) nd_thread_join(statsd.collection_threads_status[i].thread);
2411
}
2412
+ freez(statsd.collection_threads_status);
2413
}
2414
2415
collector_info("STATSD: closing sockets...");
@@ -2613,6 +2610,7 @@ void *statsd_main(void *ptr) {
2610
char tag[NETDATA_THREAD_TAG_MAX + 1];
2611
snprintfz(tag, NETDATA_THREAD_TAG_MAX, "STATSD_IN[%d]", i + 1);
2612
spinlock_init(&statsd.collection_threads_status[i].spinlock);
2613
+ statsd.collection_threads_status[i].initializing = true;
2614
statsd.collection_threads_status[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT,
2615
statsd_collector_thread, &statsd.collection_threads_status[i]);
2616
}
src/daemon/daemon-shutdown.c
+8
-4
@@ -85,9 +85,10 @@ void cancel_main_threads(void) {
85
}
86
87
for (i = 0; static_threads[i].name != NULL ; i++) {
88
- struct netdata_static_thread *st = &static_threads[i];
89
- if(st->thread && !nd_thread_is_me(static_threads[i].thread))
90
- nd_thread_join(st->thread);
88
+ if(static_threads[i].thread && !nd_thread_is_me(static_threads[i].thread)) {
89
+ if (static_threads[i].enabled == NETDATA_MAIN_THREAD_EXITED)
90
+ nd_thread_join(static_threads[i].thread);
91
+ }
92
}
93
netdata_log_info("All threads finished.");
94
@@ -197,8 +198,11 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal, bool exi
198
webrtc_close_all_connections();
199
watcher_step_complete(WATCHER_STEP_ID_CLOSE_WEBRTC_CONNECTIONS);
200
200
- service_signal_exit(SERVICE_MAINTENANCE | ABILITY_DATA_QUERIES | ABILITY_WEB_REQUESTS |
201
+ service_signal_exit(SERVICE_MAINTENANCE | ABILITY_DATA_QUERIES | ABILITY_WEB_REQUESTS | SERVICE_ACLK |
202
ABILITY_STREAMING_CONNECTIONS | SERVICE_SYSTEMD);
203
+
204
+ service_signal_exit(SERVICE_EXPORTERS | SERVICE_HEALTH | SERVICE_WEB_SERVER | SERVICE_HTTPD);
205
+
206
watcher_step_complete(WATCHER_STEP_ID_DISABLE_MAINTENANCE_NEW_QUERIES_NEW_WEB_REQUESTS_NEW_STREAMING_CONNECTIONS);
207
208
service_wait_exit(SERVICE_MAINTENANCE | SERVICE_SYSTEMD, 5 * USEC_PER_SEC);
src/database/sqlite/sqlite_metadata.c
+2
-1
@@ -1935,7 +1935,8 @@ static void ctx_hosts_load(uv_work_t *req)
1935
__atomic_store_n(&hclt[thread_index].busy, true, __ATOMIC_RELAXED);
1936
hclt[thread_index].host = host;
1937
hclt[thread_index].thread = nd_thread_create("CTXLOAD", NETDATA_THREAD_OPTION_DEFAULT, restore_host_context, &hclt[thread_index]);
1938
- async_exec += (hclt[thread_index].thread != NULL);
1938
+ rc = (hclt[thread_index].thread == NULL);
1939
+ async_exec += (rc == 0);
1940
// if it failed, mark the thread slot as free
1941
if (rc)
1942
__atomic_store_n(&hclt[thread_index].busy, false, __ATOMIC_RELAXED);
src/exporting/exporting_engine.c
+1
@@ -106,6 +106,7 @@ static void exporting_clean_engine()
106
instance = instance->next;
107
108
clean_instance(current_instance);
109
+ freez(current_instance);
110
}
111
112
freez((void *)engine->config.hostname);
src/health/health_event_loop.c
+1
-2
@@ -702,9 +702,8 @@ static void health_main_cleanup(void *pptr) {
702
703
worker_unregister();
704
static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
705
- static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
706
-
705
finalize_self_prepared_sql_statements();
706
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
707
nd_log(NDLS_DAEMON, NDLP_DEBUG, "Health thread ended.");
708
}
709
src/libnetdata/threads/threads.c
+2
-3
@@ -272,6 +272,7 @@ void nd_thread_join_threads()
272
if (nti) {
273
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
274
nti->list = ND_THREAD_LIST_NONE;
275
+ nd_log_daemon(NDLP_DEBUG, "nd_thread_join_threads: Joining thread with id %d (%s) during shutdown", nti->tid, nti->tag);
276
}
277
278
spinlock_unlock(&threads_globals.exited.spinlock);
@@ -457,10 +458,8 @@ int nd_thread_join(ND_THREAD *nti) {
458
if(!nti)
459
return ESRCH;
460
460
- if(nd_thread_status_check(nti, NETDATA_THREAD_STATUS_JOINED)) {
461
- freez(nti);
461
+ if(nd_thread_status_check(nti, NETDATA_THREAD_STATUS_JOINED))
462
return 0;
463
- }
463
464
int ret;
465
if((ret = uv_thread_join(&nti->thread))) {
src/web/server/static/static-threaded.c
+23
-3
@@ -64,7 +64,8 @@ struct web_server_static_threaded_worker {
64
ND_THREAD *thread;
65
66
int id;
67
- int running;
67
+ bool initializing;
68
+ SPINLOCK spinlock;
69
70
size_t max_sockets;
71
@@ -274,7 +275,6 @@ static void socket_listen_main_static_threaded_worker_cleanup(void *pptr) {
275
worker_private->sends
276
);
277
277
- worker_private->running = 0;
278
worker_unregister();
279
}
280
@@ -284,7 +284,9 @@ static bool web_server_should_stop(void) {
284
285
void *socket_listen_main_static_threaded_worker(void *ptr) {
286
worker_private = ptr;
287
- worker_private->running = 1;
287
+ spinlock_lock(&worker_private->spinlock);
288
+ worker_private->initializing = false;
289
+ spinlock_unlock(&worker_private->spinlock);
290
worker_register("WEB");
291
worker_register_job_name(WORKER_JOB_ADD_CONNECTION, "connect");
292
worker_register_job_name(WORKER_JOB_DEL_COLLECTION, "disconnect");
@@ -361,6 +363,20 @@ static void socket_listen_main_static_threaded_cleanup(void *pptr) {
363
listen_sockets_close(&api_sockets);
364
365
netdata_log_info("all static web threads stopped.");
366
+
367
+ // Lets join all threads
368
+ for (int i = 1; i < static_threaded_workers_count; i++) {
369
+ bool initializing;
370
+ do {
371
+ spinlock_lock(&static_workers_private_data[i].spinlock);
372
+ initializing = static_workers_private_data[i].initializing;
373
+ spinlock_unlock(&static_workers_private_data[i].spinlock);
374
+ if (unlikely(initializing))
375
+ sleep_usec(1000);
376
+ } while(initializing);
377
+ (void) nd_thread_join(static_workers_private_data[i].thread);
378
+ }
379
+
380
static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
381
}
382
@@ -387,6 +403,8 @@ void *socket_listen_main_static_threaded(void *ptr) {
403
sizeof(struct web_server_static_threaded_worker));
404
405
int i;
406
+ spinlock_init(&static_workers_private_data[0].spinlock);
407
+ static_workers_private_data[0].initializing = true;
408
for (i = 1; i < static_threaded_workers_count; i++) {
409
static_workers_private_data[i].id = i;
410
static_workers_private_data[i].max_sockets = max_sockets / static_threaded_workers_count;
@@ -394,6 +412,8 @@ void *socket_listen_main_static_threaded(void *ptr) {
412
char tag[50 + 1];
413
snprintfz(tag, sizeof(tag) - 1, "WEB[%d]", i+1);
414
415
+ spinlock_init(&static_workers_private_data[i].spinlock);
416
+ static_workers_private_data[i].initializing = true;
417
static_workers_private_data[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT,
418
socket_listen_main_static_threaded_worker,
419
(void *)&static_workers_private_data[i]);