Context
FKVS uses nonblocking sockets and buffers outgoing responses in client_t.wbuf, but the event loops currently watch read readiness only. If send() returns EAGAIN or only writes part of a response, bytes can remain in wbuf without any write-readiness registration to flush them later.
Relevant code:
src/commands/common/command_registry.c (wbuf_append, wbuf_flush, direct send() fallback)
src/networking/networking.c (try_process_frames flushes once after reads)
src/io/event_dispatcher_epoll.c
src/io/event_dispatcher_kqueue.c
src/io/event_dispatcher_io_uring.c
Scope
- Track pending response bytes per client.
- Register write readiness (
EPOLLOUT/EVFILT_WRITE or io_uring send operations) when wbuf_used > 0.
- Disable write readiness once the buffer is fully flushed.
- Remove or make safe the direct
send() fallback in wbuf_append; all writes must handle partial sends.
- Decide how to handle clients that stay backpressured too long.
Acceptance criteria
- Large responses and pipelined command bursts do not stall under receiver backpressure.
- Partial writes are retried without response corruption or reordering.
- Tests cover forced partial writes /
EAGAIN behavior.
- No unchecked
send() path remains for framed responses.
Context
FKVS uses nonblocking sockets and buffers outgoing responses in
client_t.wbuf, but the event loops currently watch read readiness only. Ifsend()returnsEAGAINor only writes part of a response, bytes can remain inwbufwithout any write-readiness registration to flush them later.Relevant code:
src/commands/common/command_registry.c(wbuf_append,wbuf_flush, directsend()fallback)src/networking/networking.c(try_process_framesflushes once after reads)src/io/event_dispatcher_epoll.csrc/io/event_dispatcher_kqueue.csrc/io/event_dispatcher_io_uring.cScope
EPOLLOUT/EVFILT_WRITEor io_uring send operations) whenwbuf_used > 0.send()fallback inwbuf_append; all writes must handle partial sends.Acceptance criteria
EAGAINbehavior.send()path remains for framed responses.