Skip to content
Merged
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
13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,19 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>
@{argLine}
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/sun.security.x509=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.example.vault;

import com.skyflow.Skyflow;
import com.skyflow.config.Credentials;
import com.skyflow.config.VaultConfig;
import com.skyflow.enums.Env;
import com.skyflow.enums.LogLevel;
import com.skyflow.errors.SkyflowException;

/**
* This sample demonstrates how to configure HTTP timeout and retry behavior in the Skyflow Java SDK.
*
* <p>Configurable settings (all optional):
* <ul>
* <li>{@code timeout} – overall call timeout in <b>seconds</b> (bounds the whole
* request including retries and backoff). Default: 60.</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 initialRetryDelay} – base backoff before the first retry, in <b>milliseconds</b>.
* Default: 500.</li>
* <li>{@code maxRetryDelay} – cap on the (exponentially growing) backoff, in
* <b>milliseconds</b>. Default: 2000.</li>
* </ul>
*
* <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>.
*/
public class TimeoutAndRetryConfigExample {

public static void main(String[] args) {
try {
// Step 1: Initialize credentials with the path to your service account key file
String filePath = "<YOUR_CREDENTIALS_FILE_PATH>";
Credentials credentials = new Credentials();
credentials.setPath(filePath);

// Step 2: Configure the vault. Optionally override timeout/retry settings for THIS vault only.
VaultConfig vaultConfig = new VaultConfig();
vaultConfig.setVaultId("<YOUR_VAULT_ID>");
vaultConfig.setClusterId("<YOUR_CLUSTER_ID>");
vaultConfig.setEnv(Env.PROD);
vaultConfig.setCredentials(credentials);
// 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.setMaxRetries(2); // fewer retries for this vault
vaultConfig.setInitialRetryDelay(500L);
vaultConfig.setMaxRetryDelay(1000L);

// Step 3: Create the Skyflow client. Client-wide defaults apply to every vault
// unless that vault overrides them (as above).
Skyflow skyflowClient = Skyflow.builder()
.setLogLevel(LogLevel.ERROR)
.timeout(60) // seconds – client-wide overall call timeout
.maxRetries(3) // client-wide retry attempts
.initialRetryDelay(500L) // client-wide base backoff (ms)
.maxRetryDelay(2000L) // client-wide backoff cap (ms)
.addVaultConfig(vaultConfig)
.build();

// Step 4: Use the client as usual. Requests now fail fast at the configured timeout and
// retry transient 408/429/5xx responses with exponential backoff + jitter.
System.out.println("Skyflow client configured with custom timeout & retry settings: " + skyflowClient);

// Example (uncomment and fill in a real request to try it):
// DetokenizeResponse response = skyflowClient.vault().detokenize(detokenizeRequest);
// System.out.println(response);
} catch (SkyflowException e) {
// Step 5: Handle any errors that occur during the process
System.err.println("Error in Skyflow operations: " + e.getMessage());
}
}
}
43 changes: 42 additions & 1 deletion v3/src/main/java/com/skyflow/Skyflow.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public VaultController vault() throws SkyflowException {
public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder {
private final LinkedHashMap<String, VaultConfig> vaultConfigMap;
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 maxRetries;
private Long initialRetryDelay;
private Long maxRetryDelay;

public SkyflowClientBuilder() {
this.vaultConfigMap = new LinkedHashMap<>();
Expand All @@ -78,7 +83,9 @@ public SkyflowClientBuilder addVaultConfig(VaultConfig vaultConfig) throws Skyfl
ErrorMessage.VaultIdAlreadyInConfigList.getMessage());
} else {
this.vaultConfigMap.put(vaultConfigCopy.getVaultId(), vaultConfigCopy); // add new config in map
this.vaultClientsMap.put(vaultConfigCopy.getVaultId(), new VaultController(vaultConfigCopy, this.skyflowCredentials)); // add new controller with new config
VaultController controller = new VaultController(vaultConfigCopy, this.skyflowCredentials); // new controller with new config
controller.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelay, this.maxRetryDelay);
this.vaultClientsMap.put(vaultConfigCopy.getVaultId(), controller);
LogUtil.printInfoLog(Utils.parameterizedString(
InfoLogs.VAULT_CONTROLLER_INITIALIZED.getLog(), vaultConfigCopy.getVaultId()));
}
Expand All @@ -100,6 +107,40 @@ public SkyflowClientBuilder addSkyflowCredentials(Credentials credentials) throw
return this;
}

/** Client-wide overall call timeout in seconds. Applies to all vaults unless a vault overrides it. */
public SkyflowClientBuilder timeout(int timeout) {
this.timeout = timeout;
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;
propagateHttpConfig();
return this;
}

/** Client-wide base retry backoff in milliseconds. Applies to all vaults unless a vault overrides it. */
public SkyflowClientBuilder initialRetryDelay(long initialRetryDelay) {
this.initialRetryDelay = initialRetryDelay;
propagateHttpConfig();
return this;
}

/** Client-wide retry backoff cap in milliseconds. Applies to all vaults unless a vault overrides it. */
public SkyflowClientBuilder maxRetryDelay(long maxRetryDelay) {
this.maxRetryDelay = maxRetryDelay;
propagateHttpConfig();
return this;
}

private void propagateHttpConfig() {
for (VaultController vault : this.vaultClientsMap.values()) {
vault.setCommonHttpConfig(this.timeout, this.maxRetries, this.initialRetryDelay, this.maxRetryDelay);
}
}

@Override
public SkyflowClientBuilder setLogLevel(LogLevel logLevel) {
super.setLogLevel(logLevel);
Expand Down
54 changes: 53 additions & 1 deletion v3/src/main/java/com/skyflow/VaultClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import com.skyflow.config.Credentials;
Expand All @@ -19,6 +20,7 @@
import com.skyflow.generated.rest.types.V1Upsert;
import com.skyflow.logs.InfoLogs;
import com.skyflow.serviceaccount.util.Token;
import com.skyflow.generated.rest.core.RetryInterceptor;
import com.skyflow.utils.Constants;
import com.skyflow.utils.Utils;
import com.skyflow.utils.logger.LogUtil;
Expand All @@ -45,6 +47,16 @@ public class VaultClient {
private String apiKey;
private OkHttpClient sharedHttpClient = null;
private String currentVaultURL = null;
// Client-wide (Skyflow builder) HTTP config defaults; null => fall back to the SDK defaults below.
private Integer commonTimeout;
private Integer commonMaxRetries;
private Long commonInitialRetryDelay;
private Long commonMaxRetryDelay;
// SDK defaults, used when neither the vault-level nor the client-wide value is set.
private static final int DEFAULT_TIMEOUT_SECONDS = 60;
private static final int DEFAULT_MAX_RETRIES = 0; // retries OFF by default (opt-in) so non-idempotent writes aren't auto-retried
private static final long DEFAULT_INITIAL_RETRY_DELAY_MILLIS = 500L;
private static final long DEFAULT_MAX_RETRY_DELAY_MILLIS = 2000L;

protected VaultClient(VaultConfig vaultConfig, Credentials credentials) throws SkyflowException {
super();
Expand All @@ -68,6 +80,20 @@ protected void setCommonCredentials(Credentials commonCredentials) throws Skyflo
prioritiseCredentials();
}

/**
* 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,
Long initialRetryDelay, Long maxRetryDelay) {
this.commonTimeout = timeout;
this.commonMaxRetries = maxRetries;
this.commonInitialRetryDelay = initialRetryDelay;
this.commonMaxRetryDelay = maxRetryDelay;
this.sharedHttpClient = null;
this.apiClient = null;
}

protected void setBearerToken() throws SkyflowException {
prioritiseCredentials();
Validations.validateCredentials(this.finalCredentials);
Expand Down Expand Up @@ -143,9 +169,19 @@ private void prioritiseCredentials() throws SkyflowException {

protected void updateExecutorInHTTP() {
if (sharedHttpClient == null) {
int timeoutSeconds = resolveInt(vaultConfig.getTimeout(), commonTimeout, DEFAULT_TIMEOUT_SECONDS);
int maxRetries = resolveInt(vaultConfig.getMaxRetries(), commonMaxRetries, DEFAULT_MAX_RETRIES);
long initialRetryDelay = resolveLong(
vaultConfig.getInitialRetryDelay(), commonInitialRetryDelay, DEFAULT_INITIAL_RETRY_DELAY_MILLIS);
long maxRetryDelay = resolveLong(
vaultConfig.getMaxRetryDelay(), commonMaxRetryDelay, DEFAULT_MAX_RETRY_DELAY_MILLIS);

sharedHttpClient = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(10, 1, TimeUnit.MINUTES))
.addInterceptor(chain -> {
.callTimeout(timeoutSeconds, TimeUnit.SECONDS) // overall ceiling; bounds the whole call incl. retries
.addInterceptor(new RetryInterceptor( // OUTER: retries (Fern generated; jitter default 0.2)
maxRetries, Optional.of(initialRetryDelay), Optional.of(maxRetryDelay), Optional.empty()))
.addInterceptor(chain -> { // INNER: auth (reads this.token per request)
Request requestWithAuth = chain.request().newBuilder()
.header("Authorization", "Bearer " + this.token)
.build();
Expand All @@ -156,6 +192,22 @@ protected void updateExecutorInHTTP() {
}
}

/** Resolve an int setting: vault-level override, else client-wide default, else SDK default. */
private static int resolveInt(Integer vaultLevel, Integer clientLevel, int defaultValue) {
if (vaultLevel != null) {
return vaultLevel;
}
return clientLevel != null ? clientLevel : defaultValue;
}

/** 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) {
return vaultLevel;
}
return clientLevel != null ? clientLevel : defaultValue;
}

protected V1InsertRequest getBulkInsertRequestBody(com.skyflow.vault.data.InsertRequest request, VaultConfig config) {
ArrayList<InsertRecord> records = request.getRecords();
List<V1InsertRecordData> insertRecordDataList = new ArrayList<>();
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 @@ -8,13 +8,22 @@ public class VaultConfig implements Cloneable {
private String vaultURL;
private Env env;
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 maxRetries; // retry attempts after the first failure
private Long initialRetryDelay; // base backoff before the first retry, in ms
private Long maxRetryDelay; // cap on the (exponentially growing) backoff, in ms

public VaultConfig() {
this.vaultId = null;
this.clusterId = null;
this.vaultURL = null;
this.env = Env.PROD;
this.credentials = null;
this.timeout = null;
this.maxRetries = null;
this.initialRetryDelay = null;
this.maxRetryDelay = null;
}

public String getVaultId() {
Expand Down Expand Up @@ -57,6 +66,42 @@ public void setVaultURL(String vaultURL) {
this.vaultURL = vaultURL;
}

public Integer getTimeout() {
return timeout;
}

/** Overall call timeout in seconds for this vault. Overrides the client-wide default. */
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}

public Integer getMaxRetries() {
return maxRetries;
}

/** Number of retry attempts after the first failure for this vault. Overrides the client-wide default. */
public void setMaxRetries(Integer maxRetries) {
this.maxRetries = maxRetries;
}

public Long getInitialRetryDelay() {
return initialRetryDelay;
}

/** Base retry backoff in milliseconds for this vault. Overrides the client-wide default. */
public void setInitialRetryDelay(Long initialRetryDelay) {
this.initialRetryDelay = initialRetryDelay;
}

public Long getMaxRetryDelay() {
return maxRetryDelay;
}

/** Cap on retry backoff in milliseconds for this vault. Overrides the client-wide default. */
public void setMaxRetryDelay(Long maxRetryDelay) {
this.maxRetryDelay = maxRetryDelay;
}

@Override
public Object clone() throws CloneNotSupportedException {
VaultConfig cloned = (VaultConfig) super.clone();
Expand Down
Loading
Loading