Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,19 @@ You can control how long a request is allowed to run and whether failed requests
| Setting | Unit | Default | Description |
| --- | --- | --- | --- |
| `timeout` | seconds | `60` | Overall time budget for a request, including any retries and backoff. |
| `connectTimeout` | seconds | `10` | Time budget for establishing a connection, per attempt. |
| `readTimeout` | seconds | `10` | Time budget for reading the response, per attempt. |
| `writeTimeout` | seconds | `10` | Time budget for writing the request, per attempt. |
| `maxRetries` | count | `0` | Number of retry attempts. `0` disables retries. |
| `initialRetryDelayMillis` | milliseconds | `500` | Base delay before the first retry. |
| `maxRetryDelayMillis` | milliseconds | `2000` | Upper bound on the delay between retries. |

**How the four timeouts relate:** `timeout` is the *total* ceiling for the whole call — every attempt, every backoff sleep, all counted together. `connectTimeout`, `readTimeout`, and `writeTimeout` each bound a single *phase of one attempt* (connecting, reading, writing). Because the phase timeouts are per attempt, their sum across retries can exceed `timeout` — but `timeout` always wins and cuts the call off. In practice, set `timeout` to cap worst-case blocking, and use the phase timeouts to fail faster on a stalled connection or a slow-but-not-dead server. The three phase timeouts default to the underlying HTTP client's `10` seconds; leaving them unset preserves that default.

Each setting is available at two levels:

- **Client-wide** — on `Skyflow.builder()`: `.timeout(int)`, `.maxRetries(int)`, `.initialRetryDelayMillis(long)`, `.maxRetryDelayMillis(long)`. Applies to every vault.
- **Per vault** — on `VaultConfig`: `.setTimeout(int)`, `.setMaxRetries(int)`, `.setInitialRetryDelayMillis(long)`, `.setMaxRetryDelayMillis(long)`. Applies to that vault only.
- **Client-wide** — on `Skyflow.builder()`: `.timeout(int)`, `.connectTimeout(int)`, `.readTimeout(int)`, `.writeTimeout(int)`, `.maxRetries(int)`, `.initialRetryDelayMillis(long)`, `.maxRetryDelayMillis(long)`. Applies to every vault.
- **Per vault** — on `VaultConfig`: `.setTimeout(int)`, `.setConnectTimeout(int)`, `.setReadTimeout(int)`, `.setWriteTimeout(int)`, `.setMaxRetries(int)`, `.setInitialRetryDelayMillis(long)`, `.setMaxRetryDelayMillis(long)`. Applies to that vault only.

**Precedence:** a value set on `VaultConfig` (per vault) overrides the client-wide value set on `Skyflow.builder()`, which overrides the SDK default. Resolution is per field, so a vault can override just `timeout` and still inherit the client-wide retry settings.

Expand All @@ -206,21 +211,28 @@ vaultConfig.setClusterId("<CLUSTER_ID>");
vaultConfig.setEnv(Env.PROD);
vaultConfig.setCredentials(credentials);
vaultConfig.setTimeout(30); // seconds — overall request timeout
vaultConfig.setConnectTimeout(5); // seconds — per-attempt connect timeout
vaultConfig.setReadTimeout(20); // seconds — per-attempt read timeout
vaultConfig.setWriteTimeout(5); // seconds — per-attempt write timeout
vaultConfig.setMaxRetries(3); // retry attempts (0 = retries off)
vaultConfig.setInitialRetryDelayMillis(1000L); // base backoff in milliseconds
vaultConfig.setMaxRetryDelayMillis(4000L); // backoff cap in milliseconds

// Client-wide defaults: apply to every vault unless overridden on the vault (as above).
Skyflow skyflowClient = Skyflow.builder()
.timeout(60) // seconds — overall request timeout
.connectTimeout(10) // seconds — per-attempt connect timeout
.readTimeout(15) // seconds — per-attempt read timeout
.writeTimeout(10) // seconds — per-attempt write timeout
.maxRetries(2) // retry attempts (0 = retries off)
.initialRetryDelayMillis(500L) // base backoff in milliseconds
.maxRetryDelayMillis(2000L) // backoff cap in milliseconds
.addVaultConfig(vaultConfig)
.build();

// Result for this vault: timeout=30, maxRetries=3, initialRetryDelayMillis=1000, maxRetryDelayMillis=4000
// (all overridden per vault). A vault that sets none of these inherits the client-wide values above.
// Result for this vault: timeout=30, connectTimeout=5, readTimeout=20, writeTimeout=5,
// maxRetries=3, initialRetryDelayMillis=1000, maxRetryDelayMillis=4000 (all overridden per vault).
// A vault that sets none of these inherits the client-wide values above.
```

# Vault
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
* <ul>
* <li>{@code timeout} – overall call timeout in <b>seconds</b> (bounds the whole
* request including retries and backoff). Default: 60.</li>
* <li>{@code connectTimeout} – per-attempt connection-establishment timeout in
* <b>seconds</b>. Default: 10 (the underlying HTTP client default).</li>
* <li>{@code readTimeout} – per-attempt response-read timeout in <b>seconds</b>.
* Default: 10.</li>
* <li>{@code writeTimeout} – per-attempt request-write timeout in <b>seconds</b>.
* Default: 10.</li>
* <li>{@code maxRetries} – retry attempts after the first failure (retries on HTTP
* 408 / 429 / 5xx). Default: 0 — retries are OFF unless you set this (avoids auto-retrying non-idempotent writes).</li>
* <li>{@code initialRetryDelayMillis} – base backoff before the first retry, in <b>milliseconds</b>.
Expand All @@ -22,6 +28,11 @@
* <b>milliseconds</b>. Default: 2000.</li>
* </ul>
*
* <p><b>How they relate:</b> {@code timeout} is the <i>total</i> ceiling for the whole call (all
* attempts + backoff). {@code connectTimeout}/{@code readTimeout}/{@code writeTimeout} each bound a
* single <i>phase of one attempt</i>; because they are per attempt, their sum across retries can
* exceed {@code timeout}, but {@code timeout} always wins and cuts the call off.
*
* <p><b>Two levels + precedence:</b> set client-wide defaults on {@code Skyflow.builder()}, and/or
* per-vault overrides on {@code VaultConfig}. The most specific value wins, resolved per field:
* <b>per-vault &rarr; client-wide &rarr; SDK default</b>.
Expand All @@ -44,6 +55,9 @@ public static void main(String[] args) {
// Per-vault overrides (optional). Any field left unset inherits the client-wide default below,
// and then the SDK default.
vaultConfig.setTimeout(30); // seconds – tighter overall ceiling for this vault
vaultConfig.setConnectTimeout(5); // seconds – fail fast if the connection stalls
vaultConfig.setReadTimeout(20); // seconds – allow a slower response read
vaultConfig.setWriteTimeout(5); // seconds – bound the request write
vaultConfig.setMaxRetries(2); // fewer retries for this vault
vaultConfig.setInitialRetryDelayMillis(500L);
vaultConfig.setMaxRetryDelayMillis(1000L);
Expand All @@ -53,6 +67,9 @@ public static void main(String[] args) {
Skyflow skyflowClient = Skyflow.builder()
.setLogLevel(LogLevel.ERROR)
.timeout(60) // seconds – client-wide overall call timeout
.connectTimeout(10) // seconds – client-wide per-attempt connect timeout
.readTimeout(15) // seconds – client-wide per-attempt read timeout
.writeTimeout(10) // seconds – client-wide per-attempt write timeout
.maxRetries(3) // client-wide retry attempts
.initialRetryDelayMillis(500L) // client-wide base backoff (ms)
.maxRetryDelayMillis(2000L) // client-wide backoff cap (ms)
Expand Down
42 changes: 40 additions & 2 deletions v3/src/main/java/com/skyflow/Skyflow.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder
private final LinkedHashMap<String, VaultController> vaultClientsMap;
// Client-wide HTTP config defaults (apply to all vaults unless a vault overrides). null => SDK default.
private Integer timeout;
private Integer connectTimeout;
private Integer readTimeout;
private Integer writeTimeout;
private Integer maxRetries;
private Long initialRetryDelayMillis;
private Long maxRetryDelayMillis;
Expand Down Expand Up @@ -84,7 +87,8 @@ public SkyflowClientBuilder addVaultConfig(VaultConfig vaultConfig) throws Skyfl
} else {
this.vaultConfigMap.put(vaultConfigCopy.getVaultId(), vaultConfigCopy); // add new config in map
VaultController controller = new VaultController(vaultConfigCopy, this.skyflowCredentials); // new controller with new config
controller.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis);
controller.setCommonHttpConfig(this.timeout, this.connectTimeout, this.readTimeout,
this.writeTimeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis);
this.vaultClientsMap.put(vaultConfigCopy.getVaultId(), controller);
LogUtil.printInfoLog(Utils.parameterizedString(
InfoLogs.VAULT_CONTROLLER_INITIALIZED.getLog(), vaultConfigCopy.getVaultId()));
Expand Down Expand Up @@ -114,6 +118,39 @@ public SkyflowClientBuilder timeout(int timeout) {
return this;
}

/**
* Client-wide per-attempt connection-establishment timeout in seconds. Applies to all vaults
* unless a vault overrides it; when unset the underlying HTTP client default (10s) applies.
* The overall {@code timeout} still bounds the whole call, including retries.
*/
public SkyflowClientBuilder connectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
propagateHttpConfig();
return this;
}

/**
* Client-wide per-attempt response-read timeout in seconds. Applies to all vaults unless a
* vault overrides it; when unset the underlying HTTP client default (10s) applies. The overall
* {@code timeout} still bounds the whole call, including retries.
*/
public SkyflowClientBuilder readTimeout(int readTimeout) {
this.readTimeout = readTimeout;
propagateHttpConfig();
return this;
}

/**
* Client-wide per-attempt request-write timeout in seconds. Applies to all vaults unless a
* vault overrides it; when unset the underlying HTTP client default (10s) applies. The overall
* {@code timeout} still bounds the whole call, including retries.
*/
public SkyflowClientBuilder writeTimeout(int writeTimeout) {
this.writeTimeout = writeTimeout;
propagateHttpConfig();
return this;
}

/** Client-wide retry attempt count. Applies to all vaults unless a vault overrides it. */
public SkyflowClientBuilder maxRetries(int maxRetries) {
this.maxRetries = maxRetries;
Expand All @@ -137,7 +174,8 @@ public SkyflowClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) {

private void propagateHttpConfig() {
for (VaultController vault : this.vaultClientsMap.values()) {
vault.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis);
vault.setCommonHttpConfig(this.timeout, this.connectTimeout, this.readTimeout,
this.writeTimeout, this.maxRetries, this.initialRetryDelayMillis, this.maxRetryDelayMillis);
}
}

Expand Down
40 changes: 36 additions & 4 deletions v3/src/main/java/com/skyflow/VaultClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public class VaultClient {
private String currentVaultURL = null;
// Client-wide (Skyflow builder) HTTP config defaults; null => fall back to the SDK defaults below.
private Integer commonTimeout;
private Integer commonConnectTimeout;
private Integer commonReadTimeout;
private Integer commonWriteTimeout;
private Integer commonMaxRetries;
private Long commonInitialRetryDelayMillis;
private Long commonMaxRetryDelayMillis;
Expand Down Expand Up @@ -84,9 +87,13 @@ protected void setCommonCredentials(Credentials commonCredentials) throws Skyflo
* Client-wide HTTP timeout/retry defaults from the Skyflow builder. Nulls out the cached client
* so the next call rebuilds with the new values.
*/
protected void setCommonHttpConfig(Integer timeout, Integer maxRetries,
protected void setCommonHttpConfig(Integer timeout, Integer connectTimeout, Integer readTimeout,
Integer writeTimeout, Integer maxRetries,
Long initialRetryDelayMillis, Long maxRetryDelayMillis) {
this.commonTimeout = timeout;
this.commonConnectTimeout = connectTimeout;
this.commonReadTimeout = readTimeout;
this.commonWriteTimeout = writeTimeout;
this.commonMaxRetries = maxRetries;
this.commonInitialRetryDelayMillis = initialRetryDelayMillis;
this.commonMaxRetryDelayMillis = maxRetryDelayMillis;
Expand Down Expand Up @@ -175,8 +182,12 @@ protected void updateExecutorInHTTP() {
vaultConfig.getInitialRetryDelayMillis(), commonInitialRetryDelayMillis, DEFAULT_INITIAL_RETRY_DELAY_MILLIS);
long maxRetryDelayMillis = resolveLong(
vaultConfig.getMaxRetryDelayMillis(), commonMaxRetryDelayMillis, DEFAULT_MAX_RETRY_DELAY_MILLIS);
// Per-attempt timeouts: null => leave OkHttp's built-in 10s default (backward compatible).
Integer connectTimeout = resolveNullableInt(vaultConfig.getConnectTimeout(), commonConnectTimeout);
Integer readTimeout = resolveNullableInt(vaultConfig.getReadTimeout(), commonReadTimeout);
Integer writeTimeout = resolveNullableInt(vaultConfig.getWriteTimeout(), commonWriteTimeout);

sharedHttpClient = new OkHttpClient.Builder()
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(10, 1, TimeUnit.MINUTES))
.callTimeout(timeoutSeconds, TimeUnit.SECONDS) // overall ceiling; bounds the whole call incl. retries
.addInterceptor(new RetryInterceptor( // OUTER: retries (Fern generated; jitter default 0.2)
Expand All @@ -186,8 +197,18 @@ protected void updateExecutorInHTTP() {
.header("Authorization", "Bearer " + this.token)
.build();
return chain.proceed(requestWithAuth);
})
.build();
});
// Per-attempt phase timeouts; only override when explicitly configured.
if (connectTimeout != null) {
httpBuilder.connectTimeout(connectTimeout, TimeUnit.SECONDS);
}
if (readTimeout != null) {
httpBuilder.readTimeout(readTimeout, TimeUnit.SECONDS);
}
if (writeTimeout != null) {
httpBuilder.writeTimeout(writeTimeout, TimeUnit.SECONDS);
}
sharedHttpClient = httpBuilder.build();
apiClientBuilder.httpClient(sharedHttpClient);
}
}
Expand All @@ -200,6 +221,17 @@ private static int resolveInt(Integer vaultLevel, Integer clientLevel, int defau
return clientLevel != null ? clientLevel : defaultValue;
}

/**
* Resolve an optional int setting: vault-level override, else client-wide default, else null.
* Null means "not configured" — the caller leaves the underlying HTTP client default in place.
*/
private static Integer resolveNullableInt(Integer vaultLevel, Integer clientLevel) {
if (vaultLevel != null) {
return vaultLevel;
}
return clientLevel;
}

/** Resolve a long setting: vault-level override, else client-wide default, else SDK default. */
private static long resolveLong(Long vaultLevel, Long clientLevel, long defaultValue) {
if (vaultLevel != null) {
Expand Down
45 changes: 45 additions & 0 deletions v3/src/main/java/com/skyflow/config/VaultConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public class VaultConfig implements Cloneable {
private Credentials credentials;
// HTTP timeout & retry config (vault-level overrides). null => inherit client-wide default, then SDK default.
private Integer timeout; // overall call timeout, in seconds
private Integer connectTimeout; // per-attempt connection-establishment timeout, in seconds
private Integer readTimeout; // per-attempt response-read timeout, in seconds
private Integer writeTimeout; // per-attempt request-write timeout, in seconds
private Integer maxRetries; // retry attempts after the first failure
private Long initialRetryDelayMillis; // base backoff before the first retry, in ms
private Long maxRetryDelayMillis; // cap on the (exponentially growing) backoff, in ms
Expand All @@ -21,6 +24,9 @@ public VaultConfig() {
this.env = Env.PROD;
this.credentials = null;
this.timeout = null;
this.connectTimeout = null;
this.readTimeout = null;
this.writeTimeout = null;
this.maxRetries = null;
this.initialRetryDelayMillis = null;
this.maxRetryDelayMillis = null;
Expand Down Expand Up @@ -75,6 +81,45 @@ public void setTimeout(Integer timeout) {
this.timeout = timeout;
}

public Integer getConnectTimeout() {
return connectTimeout;
}

/**
* Per-attempt connection-establishment timeout in seconds for this vault. Overrides the
* client-wide default; when unset the underlying HTTP client default (10s) applies. Note the
* overall {@code timeout} still bounds the whole call, including retries.
*/
public void setConnectTimeout(Integer connectTimeout) {
this.connectTimeout = connectTimeout;
}

public Integer getReadTimeout() {
return readTimeout;
}

/**
* Per-attempt response-read timeout in seconds for this vault. Overrides the client-wide
* default; when unset the underlying HTTP client default (10s) applies. Note the overall
* {@code timeout} still bounds the whole call, including retries.
*/
public void setReadTimeout(Integer readTimeout) {
this.readTimeout = readTimeout;
}

public Integer getWriteTimeout() {
return writeTimeout;
}

/**
* Per-attempt request-write timeout in seconds for this vault. Overrides the client-wide
* default; when unset the underlying HTTP client default (10s) applies. Note the overall
* {@code timeout} still bounds the whole call, including retries.
*/
public void setWriteTimeout(Integer writeTimeout) {
this.writeTimeout = writeTimeout;
}

public Integer getMaxRetries() {
return maxRetries;
}
Expand Down
Loading
Loading