Skip to content

Step 11a: surface server errors as typed HotRodClientException - #6

Merged
rigazilla merged 1 commit into
mainfrom
feature/error-handling-11a
Sep 25, 2026
Merged

rigazilla merged 1 commit into
mainfrom
feature/error-handling-11a

Conversation

@rigazilla

Copy link
Copy Markdown
Member

Summary

Step 11a of error handling (error surfacing), per the split agreed in docs/ERROR_HANDLING_DESIGN.md (D1). Step 11b (user-decided retry) is deferred to a follow-up PR.

  • Replace bare std::runtime_error at the operation throw sites with a pure-data HotRodClientException carrying FailurePhase (BeforeSend / AfterSend / ServerError), serverStatus, triedNodes, and ownersExhausted. It still derives from std::runtime_error, so existing catches keep working.
  • Parse the server ERROR response (opcode 0x50): its length-prefixed message is drained in the read loop before the pending-request lookup, so an orphan ERROR can no longer desync the connection stream.
  • Add missing status constants (0x84 REQUEST_PARSING_ERROR, 0x87 NODE_SUSPECTED, 0x88 ILLEGAL_LIFECYCLE_STATE) to HeaderCodec.h.

Classification: two orthogonal axes (no stored retriable flag)

  • isTransient() — futility ("is a retry worth it?", the library's call).
  • outcomeUncertain() — ambiguity ("might the op have applied?", the user's call).

COMMAND_TIMEOUT (0x86) is treated as transient and outcome-uncertain. This intentionally diverges from the Java client (which does not retry it) — safe here because ambiguity is carried separately by outcomeUncertain(), so the user still decides. Rationale in docs/DECISIONS.md (2026-09-25).

Testing

  • 11 new unit tests + 3 new integration tests.
  • ConnectionUsableAfterServerError proves the stream stays in sync: trigger an ERROR on a missing cache, then PUT/GET round-trip on a real cache over the same connection.
  • Full suite green: 187 unit, 78 integration across 15 suites.

Out of scope (Step 11b, follow-up)

Defaulted RetryContext on ops, exclusion-aware selectServerForKey, proxyToNonOwner (default true), thread-safe pool/topology, triedNodes accumulation.

🤖 Generated with Claude Code

Replace bare std::runtime_error at the operation throw sites with a
pure-data HotRodClientException carrying FailurePhase (BeforeSend /
AfterSend / ServerError), serverStatus, triedNodes and ownersExhausted.
It still derives from std::runtime_error, so existing catches keep working.

Parse the server ERROR response (opcode 0x50): its length-prefixed message
is now drained in the read loop *before* the pending-request lookup, so an
orphan ERROR can no longer desync the connection stream. Add the missing
status constants (0x84, 0x87, 0x88) to HeaderCodec.h.

Classification uses two orthogonal free helpers instead of a stored flag:
isTransient() (futility — the library's call) and outcomeUncertain()
(ambiguity — the user's call). COMMAND_TIMEOUT (0x86) is treated as
transient and outcome-uncertain; this intentionally diverges from Java,
which is safe because ambiguity is carried separately.

Step 11b (user-decided retry) remains deferred.

Tests: 11 unit + 3 integration added; full suite green
(187 unit, 78 integration across 15 suites).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rigazilla
rigazilla merged commit 9f9a9f8 into main Sep 25, 2026
3 checks passed
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.

1 participant