Skip to content

[#1076] Walk every link of a connect failure where the verdict decides the retry - #1077

Open
vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issues/1076-is-worth-retrying-every-link
Open

vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issues/1076-is-worth-retrying-every-link

Conversation

@vharseko

@vharseko vharseko commented Sep 19, 2026

Copy link
Copy Markdown
Member

Fixes #1076

CachedConnection.isWorthRetrying() is the one question that decides whether a connect the database refused is waited out or reported as it stands, and it walked the getCause() and getNextException() chains of the failure to MAX_CHAIN_LENGTH (32). The count did no termination work - the visited identity set polls every throwable once, so the walk ends on its own on any chain a driver can allocate - it only decided early: a 53300, a 57P03 or a dialect-retryable link standing 33rd in the failure was never read, and the answer was not "unknown" but false, the answer of a failure that is the caller's to see.

Two callers read that false and throw on it: the borrow (CachedConnection.java:1550), which then reports a database at its limit or on its way up instead of waiting it out within POOL_TIMEOUT_PROPERTY; and the catalog connect inside a write (JDBCStorage.java:2130), where the javadoc of the method itself names the cost - the not-accepting-yet state is "the one JDBCStorage.open() has no second attempt of its own for, so a backend that meets it stays locked down until the server is restarted". This is the #961 shape on the connect road, raised in round 2 of #1004.

The change

The count arrived with #876 together with the visited set that made it redundant: that review turned the exhaustion of holdsCredentials() into a fail-closed true and gave isWorthRetrying() the identity set only, with nothing said about the count it left answering false.

Tests

testARetryableLinkPastTheOldBudgetOfTheWalkIsLookedAt drives a real borrow through the stub driver with a failure that carries the connection-limit state 33rd in its getNextException() chain - the shape of mssql-jdbc, which chains every error of one message that way - and pins the second attempt. On master it fails with the failure reported (SQLException: error 1 of the same message, one attempt); here it passes, and makes testTheWholeChainOfTheFailureIsLookedAt beside it say what its name claims.

Rebased onto master at 26defc28ef (#1009, #1010 and #1018 are in), commit 5b82d5df1b. On this head: CachedConnectionTestCase 117/117 (116 on master + this one), CatalogConnectionTestCase 18/18 - the other reader of the walk - StampConnectionTestCase 6/6 and JDBCStorageRetryTest 113/113, Skipped: 0.

@vharseko vharseko added bug jdbc java Changes to Java sources tests Test suites: fixing, enabling, un-disabling labels Sep 19, 2026
…e the verdict decides the retry

CachedConnection.isWorthRetrying() decides whether a connect the database refused is waited
out or reported as it stands, and stopped its walk of the cause and getNextException() chains
at MAX_CHAIN_LENGTH. The count did no termination work - the visited set polls every throwable
once - it only answered the link it never reached with "the caller's to see": a 53300, a 57P03
or a dialect-retryable link standing 33rd in the failure reported the connect as permanent on a
database that would have taken it a moment later, and on the catalog connect of a write left
the backend locked down until a restart.

The walk now ends where the chains end, the way the walks of JDBCStorage whose verdict a
decision reads have since OpenIdentityPlatform#1004. MAX_CHAIN_LENGTH stays for the two budgets that have a reason
to stop and say so - the fail-closed threshold of holdsCredentials() and the rebuild budget of
redactedCopy() - and its javadoc names them rather than reading as a bound on every walk.

testARetryableLinkPastTheOldBudgetOfTheWalkIsLookedAt borrows through a failure that carries
the connection-limit state 33rd in its getNextException() chain and pins the second attempt: it
fails on master with the failure reported, and passes here.
@vharseko
vharseko force-pushed the issues/1076-is-worth-retrying-every-link branch from 5f62889 to 5b82d5d Compare September 23, 2026 10:32
@vharseko

Copy link
Copy Markdown
Member Author

@maximthomas rebased onto master at 26defc28ef - the branch was conflicting - and the one [#1076] commit goes over it (5b82d5df1b). Nothing new is proposed here; this only says what the conflict was and how it was taken.

The conflict was one hunk of CachedConnection, and it was textual: #1009 added nextBackoffMs() right above the javadoc of MAX_CHAIN_LENGTH, which this PR rewrites. Both are kept - the method as master has it, then the javadoc as this PR has it. The javadoc still holds on master: holdsCredentials() and redactedCopy() are the only readers of MAX_CHAIN_LENGTH, and the walk of isWorthRetrying() now asks the dialect of #1018 (dialect.isWorthRetrying(sql)) on every link, so a connection limit an account is given of its own is found 33rd in the chain as well. The test file merged on its own, next to the cases #1009 and #1018 added.

On this head: CachedConnectionTestCase 117/117, CatalogConnectionTestCase 18/18, StampConnectionTestCase 6/6, JDBCStorageRetryTest 113/113, Skipped: 0. The description says so, and its two line references to the callers of the walk follow the code (CachedConnection.java:1550, JDBCStorage.java:2130).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug java Changes to Java sources jdbc tests Test suites: fixing, enabling, un-disabling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JDBC backend: isWorthRetrying() stops its walk at MAX_CHAIN_LENGTH, so a retryable connect failure reported past 32 links is reported as permanent

1 participant