]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Fix CPU busy-loop in fuzzy TCP client due to EV_WRITE not being cleared
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 21 Mar 2026 13:36:18 +0000 (13:36 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 21 Mar 2026 13:37:26 +0000 (13:37 +0000)
When the TCP write queue drained, EV_WRITE was left armed on the socket
watcher. Since a connected TCP socket is always writable, libev fired
EV_WRITE continuously causing 100% CPU usage. The UDP path correctly
switched to EV_READ only after writing.

- Drop EV_WRITE when write queue empties in fuzzy_tcp_write_handler
- Only arm EV_WRITE on connection established if data is actually queued
- Remove redundant per-IO fuzzy_tcp_check_pending_timeouts call (timer
  callback already handles this), which amplified the spin with O(N)
  hash scans and GHashTable allocations on every iteration

src/plugins/fuzzy_check.c

index 73ebf9cf2f9b80d6efddca530e3321d9152e9a44..f1788cfd2c533051cbb83473a06ec45b9650b293 100644 (file)
@@ -1161,9 +1161,9 @@ fuzzy_tcp_io_handler(int fd, short what, gpointer ud)
 
                        rspamd_upstream_ok(conn->server);
 
-                       /* Now wait for both read and write events */
+                       /* Start reading; only arm EV_WRITE if there's queued data */
                        rspamd_ev_watcher_reschedule(conn->event_loop, &conn->ev,
-                                                                                EV_READ | EV_WRITE);
+                                                                                g_queue_is_empty(conn->write_queue) ? EV_READ : EV_READ | EV_WRITE);
 
                        msg_debug("fuzzy_tcp: after reschedule - fd=%d, ev.io.fd=%d",
                                          conn->fd, (int) conn->ev.io.fd);
@@ -1179,11 +1179,6 @@ fuzzy_tcp_io_handler(int fd, short what, gpointer ud)
                fuzzy_tcp_read_handler(conn);
        }
 
-       /* Check for timed out pending requests */
-       if (conn->connected) {
-               fuzzy_tcp_check_pending_timeouts(conn->rule, conn->last_activity);
-       }
-
        FUZZY_TCP_RELEASE(conn);
 }
 
@@ -1267,8 +1262,8 @@ fuzzy_tcp_write_handler(struct fuzzy_tcp_connection *conn)
                }
        }
 
-       /* Queue is empty, no more data to write */
-       /* Might want to disable EV_WRITE here if needed */
+       /* Queue is empty, switch to read-only to avoid busy-looping on EV_WRITE */
+       rspamd_ev_watcher_reschedule(conn->event_loop, &conn->ev, EV_READ);
 }
 
 /**