Skip to content

Close connections after incomplete meta operations - #91

Merged
mrattle merged 1 commit into
mainfrom
c1-cancellation-safety
Oct 16, 2026
Merged

mrattle merged 1 commit into
mainfrom
c1-cancellation-safety

Conversation

@mrattle

@mrattle mrattle commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR - What are you trying to accomplish?

Prevent incomplete meta operations from leaving reusable connections with unread responses or partial commands. This covers all existing meta protocol APIs (including the newly added multi-ops).

Details - How are you making this change? What are the effects of this change?

Each of the seven meta operations holds a guard from its first write through response completion, including quiet-mode and batch MN handling. Cancellation or an early error drops the socket and its buffers without flushing pending writes. A batch that returns an error line before MN also closes the connection. The operation is not retried.

Parsed response bytes are consumed immediately, removing the stale last_read_n state. Preflight validation failures and fully consumed refusals keep the connection available for reuse.

Client::is_closed() exposes the local closed state so callers can replace the client. Later I/O on a closed client, from meta or ASCII methods, returns the new Error::ConnectionClosed. Other I/O failures remain Error::Io. The ASCII implementation is unchanged.

Breaking: Error::ConnectionClosed is a new variant. Exhaustive matches on Error must handle it.

@mrattle
mrattle marked this pull request as ready for review September 21, 2026 18:53
@mrattle
mrattle requested a review from a team as a code owner September 21, 2026 18:53
@mrattle
mrattle force-pushed the c1-cancellation-safety branch from 7b4e044 to 2a3c114 Compare September 24, 2026 15:37
@mrattle mrattle self-assigned this Sep 24, 2026
@mrattle
mrattle force-pushed the c1-cancellation-safety branch 2 times, most recently from d4aa8b1 to 17e1420 Compare September 24, 2026 16:17
mrattle pushed a commit that referenced this pull request Oct 16, 2026
PR #91 introduced OperationGuard for the meta commands only. The ASCII
commands and the client-level version, stats, flush_all and dump_keys
still wrote a request and read the reply with nothing marking the
connection unusable if the future was dropped in between, so the stale
reply became the answer to the next request on that connection.

Wrap all 18 remaining entry points in the same guard, following the
rule already used by the meta commands: validate key length before
starting the operation so a rejected key sends nothing and keeps the
connection; complete the guard after the last read and before matching
the response so fully read refusals and error lines never close it;
for no-reply commands, complete right after flush.

MetadumpIter now owns the guard as an Option. END, BADCLASS and BUSY
complete it and end the iterator, so a refusal leaves the connection
usable; a network or protocol error, or dropping the iterator before
END, drops the guard uncompleted and closes the connection. BUSY was
previously non-terminal and re-read the socket on every next() call.

The ASCII module reuses the crate-level MAX_KEY_LENGTH instead of a
duplicate constant. Doc comments and the changelog no longer say the
close applies to meta operations only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mrattle
mrattle force-pushed the c1-cancellation-safety branch from 17e1420 to c527407 Compare October 16, 2026 17:20
@mrattle
mrattle merged commit 385e91d into main Oct 16, 2026
4 checks passed
mrattle pushed a commit that referenced this pull request Oct 17, 2026
PR #91 introduced OperationGuard for the meta commands only. The ASCII
commands and the client-level version, stats, flush_all and dump_keys
still wrote a request and read the reply with nothing marking the
connection unusable if the future was dropped in between, so the stale
reply became the answer to the next request on that connection.

Wrap all 18 remaining entry points in the same guard, following the
rule already used by the meta commands: validate key length before
starting the operation so a rejected key sends nothing and keeps the
connection; complete the guard after the last read and before matching
the response so fully read refusals and error lines never close it;
for no-reply commands, complete right after flush.

MetadumpIter now owns the guard as an Option. END, BADCLASS and BUSY
complete it and end the iterator, so a refusal leaves the connection
usable; a network or protocol error, or dropping the iterator before
END, drops the guard uncompleted and closes the connection. BUSY was
previously non-terminal and re-read the socket on every next() call.

The ASCII module reuses the crate-level MAX_KEY_LENGTH instead of a
duplicate constant. Doc comments and the changelog no longer say the
close applies to meta operations only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mrattle mrattle mentioned this pull request Oct 19, 2026
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.

2 participants