Skip to content

SK-3002: Configurable timeout & retries (Java v3 SDK)#366

Open
Devesh-Skyflow wants to merge 7 commits into
v3-release/26.7.15from
devesh/SK3002-configurable-retry-and-timeout
Open

SK-3002: Configurable timeout & retries (Java v3 SDK)#366
Devesh-Skyflow wants to merge 7 commits into
v3-release/26.7.15from
devesh/SK3002-configurable-retry-and-timeout

Conversation

@Devesh-Skyflow

Copy link
Copy Markdown
Collaborator

Summary

Makes HTTP timeout and retries configurable in the Java v3 SDK (CUST-4311 / SK-3002), and fixes a regression where the injected shared OkHttpClient silently dropped the call timeout and retries.

Problem

  • v3 exposed no way to configure timeout or retries.
  • VaultClient injects its own OkHttpClient (for auth + connection pool), which put ClientOptions.build() on the custom-client branch → callTimeout = 0 (unbounded) and no retries. A stuck call blocked the thread indefinitely → thread-pool exhaustion → unresponsive service.

Changes

  • SkyflowRetryInterceptor — hand-written OkHttp interceptor replicating Fern's algorithm (exponential backoff + proportional jitter, factor 0.2 fixed / not exposed). Retries on 408 / 429 / 5xx.
  • Public flat API (no nested builders):
    • Client-wide defaults: Skyflow.builder().timeout(sec).maxRetries(n).initialRetryDelayMillis(ms).maxRetryDelayMillis(ms)
    • Per-vault overrides: VaultConfig setters
    • Precedence (per field): per-vault → client-wide → SDK default (mirrors credentials resolution)
  • VaultClient.updateExecutorInHTTP — applies callTimeout + attaches the interceptor on the shared client (never calls apiClientBuilder.timeout() to avoid the connect/write/read clobber).
  • Defaults: timeout 60s, maxRetries 3, initialRetryDelayMillis 500, maxRetryDelayMillis 2000 (worst-case backoff ≲ 3.85s, well inside the 60s ceiling).
  • Sample: TimeoutAndRetryConfigExample.

Scope

Phase 1 = call timeout + retries. connectTimeout / readTimeout / writeTimeout are deferred to a future phase (Fern can't express them at any version; they'll go on the same shared client).

Naming

Adopts Fern's vocabulary/types (maxRetries, initialRetryDelayMillis, maxRetryDelayMillis) so a future swap to Fern's generated interceptor is a drop-in. Default values differ from Fern (tighter, per team decision).

Why our own interceptor (not Fern's)

The shared client (needed for auth + connection pool) disables Fern's auto-attached RetryInterceptor, and the currently-vendored Fern class hardcodes its backoff (can't honor the delay knobs). A TODO(CUST-4311) marks it for replacement once a Fern version exposing the delay knobs is vendored.

Tests

  • SkyflowRetryInterceptorTests (6) — retry count, stop-on-success, 408/429/5xx triggers, non-retryable passthrough, zero-retries.
  • VaultClientHttpConfigTests (6) — callTimeout applied to the shared client, precedence, bounded-not-zero regression guard, VaultConfig field round-trip.
  • All 12 pass; affected existing tests (connection pool, singleton, SkyflowTests, VaultConfigTests) pass.
  • ⚠️ Pre-existing: VaultClientTests#testSetBearerTokenWithEnvCredentials fails on Java 21 (PowerMock/byte-buddy cannot instrument JDK security classes) — reproduced on base v3, unrelated to this change.

🤖 Generated with Claude Code

Devesh-Skyflow and others added 5 commits July 21, 2026 15:14
Hand-written OkHttp interceptor replicating Fern's retry algorithm
(exponential backoff + proportional jitter, factor 0.2 fixed). Exposes
maxRetries / initialRetryDelayMillis / maxRetryDelayMillis; retries on
408/429/5xx. Carries a TODO to replace with Fern's generated interceptor
once a version exposing the delay knobs is vendored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- VaultConfig: vault-level timeout/maxRetries/initialRetryDelayMillis/
  maxRetryDelayMillis (nullable = inherit)
- Skyflow.SkyflowClientBuilder: flat client-wide setters + propagation to
  existing controllers (mirrors addSkyflowCredentials)
- VaultClient.updateExecutorInHTTP: set callTimeout + attach
  SkyflowRetryInterceptor; per-field precedence vault > client > SDK default
  (defaults 60s / 3 / 500ms / 2000ms)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- SkyflowRetryInterceptorTests: retry count, stop-on-success, 408/429/5xx
  triggers, non-retryable passthrough, zero-retries
- VaultClientHttpConfigTests: callTimeout applied to shared client, precedence
  (vault > client > default), bounded-not-zero regression guard, VaultConfig
  field getters/setters

Note: pre-existing VaultClientTests#testSetBearerTokenWithEnvCredentials fails
on Java 21 (PowerMock byte-buddy cannot instrument JDK security classes) —
reproduced on base v3, unrelated to this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Demonstrates client-wide defaults on Skyflow.builder() and per-vault
overrides on VaultConfig, with precedence and unit notes. Compiles against
the local SDK build (samples module pins a released version, so it builds
once this change ships).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drives an OkHttpClient built like VaultClient (callTimeout + SkyflowRetry
Interceptor) against a real loopback MockWebServer:
- retries 503 then succeeds (server sees 1+retries requests)
- exhausts retries, returns last failure
- no retry on 400
- callTimeout aborts a never-responding server (~1s, not hanging) -- the fix
- observable backoff between retries

Adds com.squareup.okhttp3:mockwebserver as a test dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Devesh-Skyflow
Devesh-Skyflow changed the base branch from v3 to v3-release/26.7.15 July 21, 2026 11:06
Devesh-Skyflow and others added 2 commits July 21, 2026 16:41
MockWebServer's static init loads JDK security providers, which the
PowerMock/byte-buddy agent fails to instrument on Java 21
(IllegalClassFormatException: Unsupported class file major version 65) ->
ExceptionInInitializerError in setUp. This is the same agent/JDK
incompatibility that already fails testSetBearerTokenWithEnvCredentials.

Retry/timeout behavior remains covered by the mocked-Chain unit tests
(SkyflowRetryInterceptorTests) and the config/precedence tests
(VaultClientHttpConfigTests), both green in CI. Also drops the
mockwebserver test dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the previously-untested new code paths flagged by Codecov:
- Skyflow.builder() setters + propagateHttpConfig loop (config set before
  and after addVaultConfig) -> SkyflowClientBuilderHttpConfigTests
- resolveInt/resolveLong vault + client branches via maxRetries + backoff
  precedence, asserted on the built SkyflowRetryInterceptor's fields
- interceptor InterruptedException-during-backoff path (throws IOException,
  preserves interrupt flag)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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