Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
* 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>.
* <li>{@code initialRetryDelayMillis} – base backoff before the first retry, in <b>milliseconds</b>.
* Default: 500.</li>
* <li>{@code maxRetryDelay} – cap on the (exponentially growing) backoff, in
* <li>{@code maxRetryDelayMillis} – cap on the (exponentially growing) backoff, in
* <b>milliseconds</b>. Default: 2000.</li>
* </ul>
*
Expand All @@ -45,17 +45,17 @@ public static void main(String[] args) {
// 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);
vaultConfig.setInitialRetryDelayMillis(500L);
vaultConfig.setMaxRetryDelayMillis(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)
.initialRetryDelayMillis(500L) // client-wide base backoff (ms)
.maxRetryDelayMillis(2000L) // client-wide backoff cap (ms)
.addVaultConfig(vaultConfig)
.build();

Expand Down
16 changes: 8 additions & 8 deletions v3/src/main/java/com/skyflow/Skyflow.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder
// 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;
private Long initialRetryDelayMillis;
private Long maxRetryDelayMillis;

public SkyflowClientBuilder() {
this.vaultConfigMap = new LinkedHashMap<>();
Expand All @@ -84,7 +84,7 @@ 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.initialRetryDelay, this.maxRetryDelay);
controller.setCommonHttpConfig(this.timeout, 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 @@ -122,22 +122,22 @@ public SkyflowClientBuilder maxRetries(int maxRetries) {
}

/** Client-wide base retry backoff in milliseconds. Applies to all vaults unless a vault overrides it. */
public SkyflowClientBuilder initialRetryDelay(long initialRetryDelay) {
this.initialRetryDelay = initialRetryDelay;
public SkyflowClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) {
this.initialRetryDelayMillis = initialRetryDelayMillis;
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;
public SkyflowClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) {
this.maxRetryDelayMillis = maxRetryDelayMillis;
propagateHttpConfig();
return this;
}

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

Expand Down
20 changes: 10 additions & 10 deletions v3/src/main/java/com/skyflow/VaultClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public class VaultClient {
// 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;
private Long commonInitialRetryDelayMillis;
private Long commonMaxRetryDelayMillis;
// 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
Expand Down Expand Up @@ -85,11 +85,11 @@ protected void setCommonCredentials(Credentials commonCredentials) throws Skyflo
* so the next call rebuilds with the new values.
*/
protected void setCommonHttpConfig(Integer timeout, Integer maxRetries,
Long initialRetryDelay, Long maxRetryDelay) {
Long initialRetryDelayMillis, Long maxRetryDelayMillis) {
this.commonTimeout = timeout;
this.commonMaxRetries = maxRetries;
this.commonInitialRetryDelay = initialRetryDelay;
this.commonMaxRetryDelay = maxRetryDelay;
this.commonInitialRetryDelayMillis = initialRetryDelayMillis;
this.commonMaxRetryDelayMillis = maxRetryDelayMillis;
this.sharedHttpClient = null;
this.apiClient = null;
}
Expand Down Expand Up @@ -171,16 +171,16 @@ 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);
long initialRetryDelayMillis = resolveLong(
vaultConfig.getInitialRetryDelayMillis(), commonInitialRetryDelayMillis, DEFAULT_INITIAL_RETRY_DELAY_MILLIS);
long maxRetryDelayMillis = resolveLong(
vaultConfig.getMaxRetryDelayMillis(), commonMaxRetryDelayMillis, DEFAULT_MAX_RETRY_DELAY_MILLIS);

sharedHttpClient = 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)
maxRetries, Optional.of(initialRetryDelay), Optional.of(maxRetryDelay), Optional.empty()))
maxRetries, Optional.of(initialRetryDelayMillis), Optional.of(maxRetryDelayMillis), Optional.empty()))
.addInterceptor(chain -> { // INNER: auth (reads this.token per request)
Request requestWithAuth = chain.request().newBuilder()
.header("Authorization", "Bearer " + this.token)
Expand Down
24 changes: 12 additions & 12 deletions v3/src/main/java/com/skyflow/config/VaultConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public class VaultConfig implements Cloneable {
// 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
private Long initialRetryDelayMillis; // base backoff before the first retry, in ms
private Long maxRetryDelayMillis; // cap on the (exponentially growing) backoff, in ms

public VaultConfig() {
this.vaultId = null;
Expand All @@ -22,8 +22,8 @@ public VaultConfig() {
this.credentials = null;
this.timeout = null;
this.maxRetries = null;
this.initialRetryDelay = null;
this.maxRetryDelay = null;
this.initialRetryDelayMillis = null;
this.maxRetryDelayMillis = null;
}

public String getVaultId() {
Expand Down Expand Up @@ -84,22 +84,22 @@ public void setMaxRetries(Integer maxRetries) {
this.maxRetries = maxRetries;
}

public Long getInitialRetryDelay() {
return initialRetryDelay;
public Long getInitialRetryDelayMillis() {
return initialRetryDelayMillis;
}

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

public Long getMaxRetryDelay() {
return maxRetryDelay;
public Long getMaxRetryDelayMillis() {
return maxRetryDelayMillis;
}

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

@Override
Expand Down
33 changes: 25 additions & 8 deletions v3/src/main/java/com/skyflow/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.github.cdimascio.dotenv.Dotenv;
import io.github.cdimascio.dotenv.DotenvException;

import java.io.InterruptedIOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
Expand Down Expand Up @@ -106,6 +107,26 @@ private static String extractRequestId(Map<String, List<String>> headers) {
return (ids == null || ids.isEmpty()) ? null : ids.get(0);
}

private static final int HTTP_REQUEST_TIMEOUT = 408;
private static final String REQUEST_TIMED_OUT_MESSAGE = "Request timed out.";

/**
* Builds the {@link ErrorRecord} for a network-layer failure (a batch future that
* completed exceptionally with a non-API exception). A client-side timeout — OkHttp's
* call timeout surfaces as {@link InterruptedIOException} ({@link java.net.SocketTimeoutException}
* is a subclass) anywhere in the cause chain — is reported as HTTP 408 with a clean
* message, so callers can tell a timeout apart from a server 500. Any other network
* error keeps the existing 500 behaviour.
*/
private static ErrorRecord networkErrorRecord(Throwable ex, int index) {
for (Throwable t = ex; t != null; t = t.getCause()) {
if (t instanceof InterruptedIOException) {
return new ErrorRecord(index, REQUEST_TIMED_OUT_MESSAGE, HTTP_REQUEST_TIMEOUT);
}
}
return new ErrorRecord(index, ex.getMessage(), 500);
}

public static List<ErrorRecord> handleBatchException(
Throwable ex, List<V1InsertRecordData> batch, int batchNumber, int batchSize
) {
Expand Down Expand Up @@ -153,8 +174,7 @@ public static List<ErrorRecord> handleBatchException(
} else {
int indexNumber = batchNumber > 0 ? batchNumber * batchSize : 0;
for (int j = 0; j < batch.size(); j++) {
ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500);
errorRecords.add(err);
errorRecords.add(networkErrorRecord(ex, indexNumber));
indexNumber++;
}
}
Expand Down Expand Up @@ -210,8 +230,7 @@ public static List<ErrorRecord> handleDetokenizeBatchException(
} else {
int indexNumber = batchNumber * batchSize;
for (int j = 0; j < batch.getTokens().get().size(); j++) {
ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500);
errorRecords.add(err);
errorRecords.add(networkErrorRecord(ex, indexNumber));
indexNumber++;
}
}
Expand Down Expand Up @@ -398,8 +417,7 @@ public static List<ErrorRecord> handleDeleteTokensBatchException(
} else {
int indexNumber = batchNumber * batchSize;
for (int j = 0; j < batch.getTokens().get().size(); j++) {
ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500);
errorRecords.add(err);
errorRecords.add(networkErrorRecord(ex, indexNumber));
indexNumber++;
}
}
Expand Down Expand Up @@ -508,8 +526,7 @@ public static List<ErrorRecord> handleTokenizeBatchException(
int indexNumber = batchNumber * batchSize;
int batchDataSize = batch.getData().isPresent() ? batch.getData().get().size() : 0;
for (int j = 0; j < batchDataSize; j++) {
ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500);
errorRecords.add(err);
errorRecords.add(networkErrorRecord(ex, indexNumber));
indexNumber++;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,30 @@ public void clientWideConfigSetBeforeAddVaultReachesController() throws Exceptio
Skyflow client = Skyflow.builder()
.timeout(30)
.maxRetries(2)
.initialRetryDelay(300L)
.maxRetryDelay(1500L)
.initialRetryDelayMillis(300L)
.maxRetryDelayMillis(1500L)
.addVaultConfig(vaultConfig())
.build();

VaultController controller = client.vault();
Assert.assertEquals(Integer.valueOf(30), commonField(controller, "commonTimeout"));
Assert.assertEquals(Integer.valueOf(2), commonField(controller, "commonMaxRetries"));
Assert.assertEquals(Long.valueOf(300L), commonField(controller, "commonInitialRetryDelay"));
Assert.assertEquals(Long.valueOf(1500L), commonField(controller, "commonMaxRetryDelay"));
Assert.assertEquals(Long.valueOf(300L), commonField(controller, "commonInitialRetryDelayMillis"));
Assert.assertEquals(Long.valueOf(1500L), commonField(controller, "commonMaxRetryDelayMillis"));
}

@Test
public void clientWideConfigSetAfterAddVaultPropagatesToExistingController() throws Exception {
Skyflow.SkyflowClientBuilder builder = Skyflow.builder().addVaultConfig(vaultConfig());
// Set config AFTER the controller already exists -> exercises propagateHttpConfig's loop.
builder.timeout(45).maxRetries(4).initialRetryDelay(700L).maxRetryDelay(3000L);
builder.timeout(45).maxRetries(4).initialRetryDelayMillis(700L).maxRetryDelayMillis(3000L);
Skyflow client = builder.build();

VaultController controller = client.vault();
Assert.assertEquals(Integer.valueOf(45), commonField(controller, "commonTimeout"));
Assert.assertEquals(Integer.valueOf(4), commonField(controller, "commonMaxRetries"));
Assert.assertEquals(Long.valueOf(700L), commonField(controller, "commonInitialRetryDelay"));
Assert.assertEquals(Long.valueOf(3000L), commonField(controller, "commonMaxRetryDelay"));
Assert.assertEquals(Long.valueOf(700L), commonField(controller, "commonInitialRetryDelayMillis"));
Assert.assertEquals(Long.valueOf(3000L), commonField(controller, "commonMaxRetryDelayMillis"));
}

@Test
Expand All @@ -66,7 +66,7 @@ public void noClientWideConfigLeavesCommonFieldsNull() throws Exception {
VaultController controller = client.vault();
Assert.assertNull(commonField(controller, "commonTimeout"));
Assert.assertNull(commonField(controller, "commonMaxRetries"));
Assert.assertNull(commonField(controller, "commonInitialRetryDelay"));
Assert.assertNull(commonField(controller, "commonMaxRetryDelay"));
Assert.assertNull(commonField(controller, "commonInitialRetryDelayMillis"));
Assert.assertNull(commonField(controller, "commonMaxRetryDelayMillis"));
}
}
16 changes: 8 additions & 8 deletions v3/src/test/java/com/skyflow/VaultClientHttpConfigTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,18 @@ public void vaultConfigStoresHttpFieldsAndDefaultsToNull() throws SkyflowExcepti
VaultConfig cfg = new VaultConfig();
Assert.assertNull(cfg.getTimeout());
Assert.assertNull(cfg.getMaxRetries());
Assert.assertNull(cfg.getInitialRetryDelay());
Assert.assertNull(cfg.getMaxRetryDelay());
Assert.assertNull(cfg.getInitialRetryDelayMillis());
Assert.assertNull(cfg.getMaxRetryDelayMillis());

cfg.setTimeout(30);
cfg.setMaxRetries(2);
cfg.setInitialRetryDelay(250L);
cfg.setMaxRetryDelay(1500L);
cfg.setInitialRetryDelayMillis(250L);
cfg.setMaxRetryDelayMillis(1500L);

Assert.assertEquals(Integer.valueOf(30), cfg.getTimeout());
Assert.assertEquals(Integer.valueOf(2), cfg.getMaxRetries());
Assert.assertEquals(Long.valueOf(250L), cfg.getInitialRetryDelay());
Assert.assertEquals(Long.valueOf(1500L), cfg.getMaxRetryDelay());
Assert.assertEquals(Long.valueOf(250L), cfg.getInitialRetryDelayMillis());
Assert.assertEquals(Long.valueOf(1500L), cfg.getMaxRetryDelayMillis());
}

private RetryInterceptor retryInterceptor(VaultClient client) throws Exception {
Expand Down Expand Up @@ -145,8 +145,8 @@ public void defaultRetryConfigAppliedToInterceptor() throws Exception {
public void vaultLevelRetryConfigOverridesDefault() throws Exception {
VaultConfig cfg = apiKeyConfig();
cfg.setMaxRetries(1);
cfg.setInitialRetryDelay(250L);
cfg.setMaxRetryDelay(1200L);
cfg.setInitialRetryDelayMillis(250L);
cfg.setMaxRetryDelayMillis(1200L);
VaultClient client = new VaultClient(cfg, cfg.getCredentials());
client.setBearerToken();

Expand Down
Loading
Loading