Skip to content

[#932] Hand the bounds of a catalog connect in, so its cases stop setting them for the whole jvm - #1006

Open
vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issues/932-catalog-connect-bounds
Open

vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issues/932-catalog-connect-bounds

Conversation

@vharseko

@vharseko vharseko commented Sep 9, 2026

Copy link
Copy Markdown
Member

Fixes #932

Problem

newCatalogConnection() read its two bounds from system properties -
org.openidentityplatform.opendj.jdbc.connect.timeout for one attempt,
org.openidentityplatform.opendj.jdbc.pool.timeout for the deadline of the whole wait - so a case
asserting on either had to set it, and all thirteen cases of CatalogConnectionTestCase did: each
saved the pair, set what it needed and put it back in a finally. For the length of a case the
value was the jvm's, and every borrow made anywhere in that jvm reads the same pair on every
borrow. testTheCatalogConnectGivesUpAtTheDeadlineOfABorrow held pool.timeout=1 for about a
second while it exhausted its deadline.

Nothing has failed for it: failsafe runs one class to a fork (reuseForks=false, parallel=none),
and both values are read once at the start of a borrow and turned into a deadline there, so a
borrow already running never sees a change. What the window needs to bite is a borrow begun
inside it on another thread - parallel cases, or a class leaving a borrower running - which is a
configuration away and would fail somewhere else entirely.

Fix

The connect takes its bounds as arguments, and the one-arg method every caller of the product uses
is the one place that reads the properties into them:

Connection newCatalogConnection(long budgetDeadline)                     // reads the pair
Connection newCatalogConnection(long budgetDeadline,                     // takes it
        long connectTimeoutSeconds, long poolTimeoutSeconds)

Nine cases hand their bounds in and touch no global. Four go on setting the properties, that being
the thing they assert - the bound reaching the driver, the default where nothing is set, both
turned off, and the deadline capping the attempt - and each holds them for one connect that is
answered at once, rather than for a retry that waits.

The pair still means the same thing for the pool and for this connect, and is still read on every
connect, so an operator can change either on a running server.

Verification

CatalogConnectionTestCase run directly under TestNG (JDK 11, one class to a jvm, the way failsafe
runs it):

run result
as it stands 13/13
with -Dorg.openidentityplatform.opendj.jdbc.connect.timeout=1 -Dorg.openidentityplatform.opendj.jdbc.pool.timeout=1 in the jvm 13/13 - the rewritten cases no longer read the ambient value
mutant: the delegate drops the pool property 1 failure (testTheCatalogConnectIsNeverBoundedPastTheDeadlineOfItsRetry)
mutant: the delegate swaps the two bounds not caught

The last row is worth stating rather than leaving to be discovered: no case can tell the two bounds
apart by what reaches the driver, attemptSeconds() taking the lesser of them, which is symmetric.
A delegation that swapped them is caught by review, not by this suite. What the four cases do pin
is that each property is read at all.

Not in this round

CachedConnectionTestCase and the container TestCase set the same properties; the first clears
them in an @AfterMethod and drives the pool through its static entry point, the second holds
connect.timeout=2 for one case beside a live backend - the only place either property stands
while a backend is serving. Both would need the same seam on CachedConnection.getConnection(),
which is 25 call sites of test churn for a risk the fork-per-class configuration already covers.

…o its cases stop setting them for the whole jvm

Fixes OpenIdentityPlatform#932

## Problem

`newCatalogConnection()` read its two bounds from system properties -
`org.openidentityplatform.opendj.jdbc.connect.timeout` for one attempt,
`org.openidentityplatform.opendj.jdbc.pool.timeout` for the deadline of the whole
wait - so a case asserting on either had to set it, and all thirteen cases of
`CatalogConnectionTestCase` did: each saved the pair, set what it needed and put it
back in a `finally`. For the length of a case the value was the jvm's, and every
borrow made anywhere in that jvm reads the same pair on every borrow.
`testTheCatalogConnectGivesUpAtTheDeadlineOfABorrow` held `pool.timeout=1` for about
a second while it exhausted its deadline.

Nothing has failed for it: failsafe runs one class to a fork
(`reuseForks=false`, `parallel=none`), and both values are read once at the start of
a borrow and turned into a deadline there, so a borrow already running never sees a
change. What the window needs to bite is a borrow *begun* inside it on another
thread - parallel cases, or a class leaving a borrower running - which is a
configuration away and would fail somewhere else entirely.

## Fix

The connect takes its bounds as arguments, and the one-arg method every caller of the
product uses is the one place that reads the properties into them:

    Connection newCatalogConnection(long budgetDeadline)                     // reads the pair
    Connection newCatalogConnection(long budgetDeadline,                     // takes it
            long connectTimeoutSeconds, long poolTimeoutSeconds)

Nine cases hand their bounds in and touch no global. Four go on setting the
properties, that being the thing they assert - the bound reaching the driver, the
default where nothing is set, both turned off, and the deadline capping the attempt -
and each holds them for one connect that is answered at once, rather than for a
retry that waits.

The pair still means the same thing for the pool and for this connect, and is still
read on every connect, so an operator can change either on a running server.

## Not in this round

`CachedConnectionTestCase` and the container `TestCase` set the same properties;
the first clears them in an `@AfterMethod` and drives the pool through its static
entry point, the second holds `connect.timeout=2` for one case beside a live
backend. Both would need the same seam on `CachedConnection.getConnection()`, which
is 25 call sites of test churn for a risk the fork-per-class configuration already
covers.

No case can tell the two bounds apart by the bound handed to the driver -
`attemptSeconds()` takes the lesser of them, which is symmetric - so a delegation
that swapped the two would be caught by review rather than by this suite. What the
four cases do pin is that each property is read at all.
@vharseko
vharseko requested a review from maximthomas September 9, 2026 17:12
@vharseko vharseko added jdbc tests Test suites: fixing, enabling, un-disabling enhancement labels Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement jdbc tests Test suites: fixing, enabling, un-disabling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JDBC backend: CatalogConnectionTestCase sets the JVM-global connect and pool timeouts while it runs

1 participant