@cryptotaxi247 / netdata-1 / commits / 679a0e26b

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;