Improve websocket thread shutdown (#21264)
Improve websocket shutdown
Stelios Fragkakis committed
Nov 5, 2025 at 21:54 UTC
679a0e26bc88bf87e3e0fa393cc0d619bd293f74
1 file changed
+54
-3
src/web/websocket/websocket-thread.c
+54
-3
@@ -484,16 +484,67 @@ void websocket_thread(void *ptr) {
484
485
// Close all clients in this thread
486
WS_CLIENT *wsc = wth->clients;
487
+
488
+ // SHUTDOWN MODE: Fast cleanup with timeouts to avoid blocking
489
+ usec_t cleanup_start = now_monotonic_usec();
490
+ usec_t max_cleanup_time = 5 * USEC_PER_SEC;
491
+ size_t clients_closed = 0, clients_skipped = 0;
492
+
493
while(wsc) {
494
WS_CLIENT *next = wsc->next;
495
490
- websocket_protocol_send_close(wsc, WS_CLOSE_GOING_AWAY, "Server shutting down");
491
- websocket_write_data(wsc);
492
- websocket_thread_remove_client(wth, wsc, true);
496
+ // Cache elapsed time to avoid repeated syscalls
497
+ usec_t elapsed = now_monotonic_usec() - cleanup_start;
498
+
499
+ if(elapsed < max_cleanup_time && wsc->sock.fd >= 0) {
500
+ bool skip_client = false;
501
+
502
+ // Ensure socket is non-blocking
503
+ int flags = fcntl(wsc->sock.fd, F_GETFL, 0);
504
+ if(flags >= 0 && !(flags & O_NONBLOCK)) {
505
+ if(fcntl(wsc->sock.fd, F_SETFL, flags | O_NONBLOCK) < 0) {
506
+ websocket_debug(wsc, "Failed to set O_NONBLOCK during shutdown: %s", strerror(errno));
507
+ skip_client = true;
508
+ }
509
+ }
510
511
+ if(!skip_client) {
512
+ // Set send timeout: 100ms max per client
513
+ struct timeval timeout = { .tv_sec = 0, .tv_usec = 100000 };
514
+ if(setsockopt(wsc->sock.fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
515
+ websocket_debug(wsc, "Failed to set SO_SNDTIMEO during shutdown: %s (continuing)", strerror(errno));
516
+ // Non-critical, continue anyway
517
+ }
518
+
519
+ // Try to send close frame (won't block due to O_NONBLOCK + SO_SNDTIMEO)
520
+ websocket_protocol_send_close(wsc, WS_CLOSE_GOING_AWAY, "Server shutting down");
521
+ ssize_t written = websocket_write_data(wsc);
522
+ if(written < 0) {
523
+ websocket_debug(wsc, "Failed to send close frame during shutdown");
524
+ }
525
+ clients_closed++;
526
+ }
527
+ else {
528
+ clients_skipped++;
529
+ }
530
+ }
531
+ else {
532
+ clients_skipped++;
533
+ }
534
+
535
+ websocket_thread_remove_client(wth, wsc, true);
536
wsc = next;
537
}
538
539
+ netdata_log_info("WEBSOCKET[%zu] shutdown cleanup complete: %zu clients closed gracefully, %zu skipped",
540
+ wth->id, clients_closed, clients_skipped);
541
+
542
+ if(clients_skipped > 0 && clients_skipped > clients_closed) {
543
+ nd_log_daemon(NDLP_WARNING, "WEBSOCKET[%zu] skipped more clients (%zu) than closed gracefully (%zu) - "
544
+ "possible network issues or timeout reached",
545
+ wth->id, clients_skipped, clients_closed);
546
+ }
547
+
548
// Reset thread's client list
549
wth->clients = NULL;
550
wth->clients_current = 0;