Repository navigation
Step 11 — error handling with user-decided retry (keyed + keyless) - #7
Merged
Merged
Conversation
Base ops never retry on their own: a failure surfaces as a typed HotRodClientException carrying the failure phase, tried nodes, and the ownersExhausted flag; the caller opts into a retry via a bound view (cache.excluding(e)), because replay-safety/idempotency is the caller's call (D2). - Exclusion-aware selectServerForKey over the pure orderKeyCandidates() helper (owners -> non-owner fallback, minus an exclusion set), reporting tried nodes and computing ownersExhausted honestly (ServerSelection.h, RetryContext.h). - Client-level proxyToNonOwner flag (default true, D5) gates the owner->non-owner fallback; disabling it throws ownersExhausted instead of proxying. - Op-level retry threading via a RetryView bound view: base signatures stay retry-free; excluding(nodes)/excluding(exception) forward to private …Impl(args, const RetryContext&) methods, and every op unions its tried nodes into the thrown exception's triedNodes (unionNodes()). - Thread-safe routing/pool state: a single stateMutex_ guards topology + consistent hash + connection pool across the read loop and user/retry threads; pool holds shared_ptr so an in-flight op's connection survives concurrent topology eviction. Fixed a concurrent push_back race in the test harness (TopologyTestFixture). - Records two intentional divergences from Java routing/retry (DECISIONS D2/D5). - RetryViewIntegrationTest (3 tests) + examples/quickstart/retry.cpp. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Keyless operations get the same two-tier model as keyed ops: automatic
before-send failover across all servers, user-decided after-send retry (D2).
- ping() delegates to pingImpl(const RetryContext&); RetryView::ping()
forwards the exclusion set, so cache.excluding(e).ping() works exactly
like keyed ops.
- selectAnyServer(ctx, triedOut) routes via orderKeyCandidates({},
allServers, exclude) (empty owners -> all servers in topology order),
sweeps to the next reachable server, and falls back to the seed connection
when no topology is known yet (ping is often the first op). ownersExhausted
is always false for keyless ops; deterministic topology order (not Java's
round-robin balancer), and after-send retry stays user-decided.
- Tests: 3 keyless-ordering unit tests; PingRetryIntegrationTest (3 tests);
a keyless pingWithRetry loop added to the retry example.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mmit - Tick Step 11c in the STATUS backlog and mark Step 11 complete. - README had drifted behind STATUS.md (pre-existing): test counts (145/46 -> 204/84 across 17 suites), Steps 10/11 shown as future though both shipped, plus the feature/milestone/headline lists. Bring all user-facing facts in line with STATUS.md. - Process fix so this drift does not recur: WORKFLOW.md §3 and AGENTS.md now prescribe checking README after *every* commit and updating it in the same commit when a user-facing fact changed (was "only if ... most commits won't touch it"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes Step 11 — error handling with a user-decided retry model, for both keyed operations and keyless
ping. Base operations never retry on their own: a failure surfaces as a typedHotRodClientExceptioncarrying the facts a caller needs (failure phase, tried nodes,ownersExhausted), and the caller opts into a retry via a bound view —cache.excluding(e)— because replay-safety/idempotency is the caller's call (D2).Three commits:
Step 11b — user-decided retry for keyed operations
selectServerForKeyover the pureorderKeyCandidates()helper (owners → non-owner fallback, minus an exclusion set), reporting tried nodes and computingownersExhaustedhonestly (ServerSelection.h,RetryContext.h).proxyToNonOwnerflag (defaulttrue, D5) gates the owner→non-owner fallback; disabling it throwsownersExhaustedinstead of proxying.RetryViewbound view: base op signatures stay retry-free;excluding(nodes)/excluding(exception)forward to private…Impl(args, const RetryContext&)methods, and every op unions its tried nodes into the thrown exception'striedNodes(unionNodes()).stateMutex_guards topology + consistent hash + connection pool across the read loop and user/retry threads; the pool holdsshared_ptrso an in-flight op's connection survives concurrent topology eviction.Step 11c — keyless-op retry (ping), mirroring the keyed strategy
ping()delegates topingImpl(const RetryContext&);RetryView::ping()forwards the exclusion set, socache.excluding(e).ping()works exactly like keyed ops.selectAnyServer(ctx, triedOut)routes viaorderKeyCandidates({}, allServers, exclude)(empty owners → all servers in topology order), sweeps to the next reachable server, and falls back to the seed connection when no topology is known yet (ping is often the first op).ownersExhaustedis alwaysfalsefor keyless ops; the implementation is generic for future non-key ops.Docs reconciliation — tick Step 11c / mark Step 11 complete in STATUS, bring the drifted README back in line with STATUS, and change WORKFLOW.md/AGENTS.md to prescribe checking README after every commit.
Divergences from Java (documented in DECISIONS.md)
maxRetries.proxyToNonOwnergates owner→non-owner fallback and, when off, surfacesownersExhaustedrather than silently proxying.COMMAND_TIMEOUT(0x86) is treated as transient + outcome-uncertain.Tests
proxyToNonOwneron/off, keyless ordering).RetryViewIntegrationTest(keyed) andPingRetryIntegrationTest(keyless ping) — verified against a live 3-node Docker cluster.examples/quickstart/retry.cppdemonstratesgetWithRetry/putWithRetry/pingWithRetry; builds warning-clean under-Wall -Wextra -Werror -pedanticand runs end-to-end.🤖 Generated with Claude Code