Conversation
Expose withReadTimeout() and withWriteTimeout() on DatabaseClientBuilder so callers can configure per-client OkHttp read and write timeouts without relying on global defaults.
There was a problem hiding this comment.
Pull request overview
Adds configurable OkHttp read/write timeouts to the MarkLogic Java client, exposing them via DatabaseClientBuilder, Spring-style property sources, and DatabaseClientFactory, and wiring the values into OkHttpServices while preserving the existing default “no timeout” behavior.
Changes:
- Added fluent builder methods and bean properties for
readTimeoutMillis/writeTimeoutMillis(default 0). - Plumbed timeouts through
DatabaseClientFactoryandDatabaseClientPropertySourceintoOkHttpServices, applying them on the OkHttp client builder when > 0. - Added/updated unit tests verifying defaults, configuration round-tripping, property parsing, and an end-to-end read-timeout firing scenario via MockWebServer.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java | Adds withReadTimeout/withWriteTimeout builder APIs that store values in millis. |
| marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java | Adds new timeout-aware newClient overload and timeout fields/accessors on Bean. |
| marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java | Adds property handlers for timeout millis and passes them through to the timeout-aware newClient overload. |
| marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java | Extends ConnectionConfig with read/write timeouts and applies them to the OkHttp client builder. |
| marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java | Adds assertions for default (0) timeouts and configured millis values. |
| marklogic-client-api/src/test/java/com/marklogic/client/impl/DatabaseClientPropertySourceTest.java | Adds tests for timeout properties from Long/String/Integer sources. |
| marklogic-client-api/src/test/java/com/marklogic/client/impl/okhttp/OkHttpTimeoutTest.java | New tests validating wiring to OkHttp and a behavioral read-timeout firing case. |
| public record ConnectionConfig(String host, int port, String basePath, String database, | ||
| SecurityContext securityContext, List<OkHttpClientConfigurator> clientConfigurators) { | ||
| SecurityContext securityContext, List<OkHttpClientConfigurator> clientConfigurators, | ||
| long readTimeoutMillis, long writeTimeoutMillis) { | ||
| } |
| if (config.readTimeoutMillis() > 0) { | ||
| clientBuilder.readTimeout(config.readTimeoutMillis(), TimeUnit.MILLISECONDS); | ||
| } | ||
| if (config.writeTimeoutMillis() > 0) { | ||
| clientBuilder.writeTimeout(config.writeTimeoutMillis(), TimeUnit.MILLISECONDS); | ||
| } |
| /** | ||
| * Verifies that a configured read timeout actually fires when the server stalls before sending | ||
| * any response. This is an end-to-end behavioral test: if the timeout were not wired to the | ||
| * OkHttpClient, the call would block indefinitely rather than throwing a SocketTimeoutException. | ||
| */ |
rjrudin
left a comment
There was a problem hiding this comment.
I recommend against doing this. A user can already configure these timeouts via DatabaseClientFactory.addConfigurator(new OkHttpClientConfigurator(...)).
So the problem identified by MLE-30245 - that the default timeouts of zero can possibly be exploited - can already be solved.
The ticket would thus need to justify the addition to the public API for the above solution. I don't think that justification exists. The main reason is that the OkHttp Builder has dozens of methods for configuring an OkHttp connection; the read and write timeouts are just two of them. There are two other timeouts as well - the connect timeout and the call timeout.
Adding these probably-very-rarely-used timeouts to public APIs creates a pattern that doesn't scale well - i.e. just how many OkHttp-specific parameters are we going to add to the public API, when a user already has a way of configuring these parameters?
|
@rjrudin It looks like configuring it with a OkHttpClientConfigurator for the DatabaseClientFactory sets the values globally. How does the scenario work when you have separate clients that you want to have different timeout values? |
It doesn't - but MLE-30245 doesn't state that as a problem to be solved. If the problem to be solved is "I need to be able to adjust the OkHttp settings for clients in an environment where multiple clients are created on different threads, and thus the existing global OkHttpClientConfigurator does not work for me" - then I would look into an approach for allowing a user to do this that doesn't involve exposing potentially dozens of OkHttp-specific settings as new configurable properties. As things stand today, the problem identified by MLE-30245 already has a solution. |
Problem
Previously, all HTTP connections made by the Java client used OkHttp's default behavior of no read or write timeout. Long-running requests could therefore block indefinitely, exhausting thread pools in multi-tenant or production applications with no mechanism to recover without a JVM restart.
Solution
Two new fluent methods on
DatabaseClientBuilderallow callers to set read and write timeouts with anyTimeUnit:withReadTimeout(long value, TimeUnit unit)withWriteTimeout(long value, TimeUnit unit)Both methods validate that the value is non-negative and that the unit is non-null, then store the timeout in milliseconds. A value of zero preserves the existing no-timeout behavior. The timeouts flow through to OkHttp only when positive, leaving the existing default unchanged.
For Spring-based applications, the same timeouts can be configured via property sources using:
marklogic.client.readTimeoutMillismarklogic.client.writeTimeoutMillisChanges
DatabaseClientBuilderwithReadTimeoutandwithWriteTimeoutmethods with input validation and@since 8.2.0JavadocDatabaseClientFactoryreadTimeoutMillis/writeTimeoutMillisfields and accessors toBean; added 8-argnewClientoverload; 6-arg overload now delegates to 8-arg with zerosDatabaseClientPropertySourcereadTimeoutMillisandwriteTimeoutMillisproperties;newClient()passes timeout values through the 8-arg overload to preserve the global handle registryOkHttpServices.ConnectionConfigreadTimeoutMillisandwriteTimeoutMillisto the recordOkHttpServices.connect()OkHttpTimeoutTestSocketTimeoutExceptionagainst a stalling MockWebServerDatabaseClientBuilderTestDatabaseClientPropertySourceTestTesting
OkHttpTimeoutTest.readTimeoutFiresWhenServerStallsuses a 100 ms client timeout against a 5-second server delay to verify that aSocketTimeoutExceptionis actually thrown, not just that a timeout value is storedBackward Compatibility
No breaking changes. All existing callers that do not configure timeouts continue to use OkHttp's no-timeout default (value of zero). The new 8-arg
newClientoverload is additive; the existing 6-arg overload delegates to it with zeros.Jira Link: https://progresssoftware.atlassian.net/browse/MLE-30245