You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
test: fix TcpProxy close/race in test_client_routes.py NLB test helper
TcpProxy.stop()/drop_connections() used to close a connection's sockets
directly, from the manager thread, while that connection's own forwarder
thread could still be blocked in select()/recv() on those exact file
descriptors -- a classic close-under-concurrent-user race. Since fds are
process-global, closing them could let the OS silently recycle the fd
number into a brand new connection before the stale forwarder thread's
blocked call unwound, causing it to read/write/close a socket that no
longer belonged to it (observed here as unhandled
"ValueError: file descriptor cannot be a negative integer (-1)" crashes
in _forward_loop once a socket was closed out from under it). stop()
also only ever joined the accept-loop thread, never the per-connection
forwarder threads it had just closed sockets out from under.
Fix, scoped entirely to this test helper (no driver code touched):
- TcpProxy._connections now maps (client_sock, target_sock) -> the
forwarder thread serving that pair.
- stop()/drop_connections() now shut down (SHUT_RDWR) both sockets --
safe to do concurrently with a blocked select()/recv(), unlike
close() -- and then join() every forwarder thread before returning.
Only the forwarder thread itself ever closes its own sockets now,
and only after it has fully stopped using them.
- _handle_new_connection starts the forwarder thread before publishing
it into _connections, so a concurrent stop()/drop_connections() can
never observe (and try to join) a thread that hasn't started yet.
- NLBEmulator.add_node()/remove_node() now serialize against each
other via an RLock, so a new proxy/connection can never be created
while another thread's remove_node() is still tearing one down.
Fixes#948.
Validation:
- Standalone stress harness (no CCM needed) driving concurrent
clients through TcpProxy while repeatedly calling
drop_connections()/stop()+restart from another thread: pre-fix,
40 iterations produced 162 unhandled ValueError crashes; post-fix,
40 iterations (same parameters) and a follow-up 150-iteration run
produced zero corruptions/exceptions/leaked threads.
- Full end-to-end runs of
TestFullNodeReplacementThroughNlb::test_should_survive_full_node_replacement_through_nlb
against a real CCM cluster, both before and after the fix, to
check for behavioral regressions and reproduce the reported
flakiness.
0 commit comments