Skip to content

fix(cpworker): serve concurrent and split control-socket requests - #304

Merged
timmy21 merged 1 commit into
0.9.xfrom
fix/260-control-socket
Oct 8, 2026
Merged

timmy21 merged 1 commit into
0.9.xfrom
fix/260-control-socket

Conversation

@timmy21

@timmy21 timmy21 commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Concurrent clients (cpworker: control socket listen backlog is 1; concurrent Go clients fail with EAGAIN #260). The control socket listened with a backlog of 1. Go dials unix sockets non-blocking, so a second client connecting while the first was pending failed at once with EAGAIN (e.g. cpdaemon's stats poll overlapping a cpctl call). The backlog is now SOMAXCONN.

  • Silent client. accept reads the handshake synchronously in the manager thread, and the socket had no receive timeout. A client that connected and never sent the handshake stalled the manager, and with it every other client. The handshake read is now bounded by SO_RCVTIMEO (1 s).

  • Request split across writes (cpworker: control socket does not frame requests by newline; split or pipelined requests get no reply #261). The code meant to wait for the rest of a request, but:

    • select() was passed fd instead of fd + 1, so the fd was never examined and the wait always timed out;
    • the newline check used the last read's length instead of the buffered length;
    • a piece arriving on the third wait was read and then reported as incomplete.

    The loop is restructured to read until the newline; most of the diff in unix_client_recv is re-indentation.

The protocol stays strict request/response. Pipelined requests (several requests in one write, or the handshake and the first request together) are not supported; the only client, cpgolib/cpworker/unix_client.go, waits for each reply before sending the next request. The constraint is documented at unix_client_recv.

Tests

New integration test TestControlSocket (cpworker/tests/integration/control_socket_test.go):

subtest before after
concurrent_clients (32 clients at once) 15 of 32 failed pass
request_split_across_writes connection reset pass
silent_client_does_not_block_others i/o timeout pass

Unit tests (9/9), the full integration suite and cpgolib tests pass.

Fixes #260

Refs #261

- listen with a SOMAXCONN backlog instead of 1, so clients that connect at
  the same time (cpdaemon's stats poll and a cpctl call) are queued rather
  than failing with EAGAIN (#260)
- bound the handshake read in accept with SO_RCVTIMEO (1 s): a client that
  connects and never sends the handshake no longer stalls the manager
  thread and every other client
- a request split across writes is now read to its newline: select() was
  passed fd instead of fd + 1, the newline check used the last read's length
  instead of the buffered length, and a piece arriving on the third wait was
  read but then reported as incomplete (#261)

The protocol stays strict request/response; pipelined requests are not
supported.

Tests: TestControlSocket integration test (concurrent clients, split
request, silent client).
@timmy21
timmy21 merged commit 58d78c6 into 0.9.x Oct 8, 2026
1 check passed
@timmy21
timmy21 deleted the fix/260-control-socket branch October 8, 2026 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cpworker: control socket listen backlog is 1; concurrent Go clients fail with EAGAIN

1 participant