@@ -46,6 +46,15 @@ public VaultController vault() throws SkyflowException {
4646
4747 public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder <VaultConfig > {
4848 private final LinkedHashMap <String , VaultController > vaultClientsMap = new LinkedHashMap <>();
49+ // Client-wide HTTP config. Resolution per vault, most specific first:
50+ // VaultConfig value -> the value set here -> SDK default (60s call timeout, 0 retries).
51+ // null here means "not set", so the SDK default applies to vaults that don't override it.
52+ // Only null means inherit: an explicit 0 is a real value and wins over the level below.
53+ private Integer timeout ;
54+ private Integer connectTimeout ;
55+ private Integer readTimeout ;
56+ private Integer writeTimeout ;
57+ private Integer maxRetries ;
4958
5059 @ Override
5160 protected void validateVaultConfig (VaultConfig vaultConfig ) throws SkyflowException {
@@ -54,13 +63,19 @@ protected void validateVaultConfig(VaultConfig vaultConfig) throws SkyflowExcept
5463
5564 @ Override
5665 protected void onVaultConfigAdded (VaultConfig vaultConfig ) throws SkyflowException {
57- this .vaultClientsMap .put (vaultConfig .getVaultId (), new VaultController (vaultConfig , this .skyflowCredentials ));
66+ VaultController controller = new VaultController (vaultConfig , this .skyflowCredentials );
67+ controller .setCommonHttpConfig (this .timeout , this .connectTimeout , this .readTimeout ,
68+ this .writeTimeout , this .maxRetries );
69+ this .vaultClientsMap .put (vaultConfig .getVaultId (), controller );
5870 LogUtil .printInfoLog (Utils .parameterizedString (InfoLogs .VAULT_CONTROLLER_INITIALIZED .getLog (), vaultConfig .getVaultId ()));
5971 }
6072
6173 @ Override
6274 protected void onVaultConfigUpdated (VaultConfig updatedConfig ) throws SkyflowException {
63- this .vaultClientsMap .put (updatedConfig .getVaultId (), new VaultController (updatedConfig , this .skyflowCredentials ));
75+ VaultController updated = new VaultController (updatedConfig , this .skyflowCredentials );
76+ updated .setCommonHttpConfig (this .timeout , this .connectTimeout , this .readTimeout ,
77+ this .writeTimeout , this .maxRetries );
78+ this .vaultClientsMap .put (updatedConfig .getVaultId (), updated );
6479 }
6580
6681 @ Override
@@ -89,9 +104,48 @@ public SkyflowClientBuilder addVaultConfig(VaultConfig vaultConfig) throws Skyfl
89104 @ Override
90105 public SkyflowClientBuilder updateVaultConfig (VaultConfig vaultConfig ) throws SkyflowException {
91106 super .updateVaultConfig (vaultConfig );
107+ carryVaultOverrides (vaultConfig );
92108 return this ;
93109 }
94110
111+ /**
112+ * BaseSkyflow.mergeVaultConfig() only carries env, clusterId and credentials across, so the
113+ * flowvault-specific fields on an incoming update — vaultURL and the HTTP settings — would
114+ * be dropped silently. Apply them to the merged config the new controller is holding. A null
115+ * on the incoming config means "leave as is", matching how the base class merges every
116+ * other field.
117+ */
118+ private void carryVaultOverrides (VaultConfig incoming ) throws SkyflowException {
119+ VaultConfig merged = this .vaultConfigMap .get (incoming .getVaultId ());
120+ if (merged == null || merged == incoming ) {
121+ return ;
122+ }
123+ if (incoming .getTimeout () != null ) {
124+ merged .setTimeout (incoming .getTimeout ());
125+ }
126+ if (incoming .getConnectTimeout () != null ) {
127+ merged .setConnectTimeout (incoming .getConnectTimeout ());
128+ }
129+ if (incoming .getReadTimeout () != null ) {
130+ merged .setReadTimeout (incoming .getReadTimeout ());
131+ }
132+ if (incoming .getWriteTimeout () != null ) {
133+ merged .setWriteTimeout (incoming .getWriteTimeout ());
134+ }
135+ if (incoming .getMaxRetries () != null ) {
136+ merged .setMaxRetries (incoming .getMaxRetries ());
137+ }
138+ // The HTTP settings above are resolved lazily on the next request, but the URL is
139+ // resolved once in the VaultClient constructor — which already ran with the old value.
140+ if (incoming .getVaultURL () != null ) {
141+ merged .setVaultURL (incoming .getVaultURL ());
142+ VaultController controller = this .vaultClientsMap .get (incoming .getVaultId ());
143+ if (controller != null ) {
144+ controller .refreshVaultURL ();
145+ }
146+ }
147+ }
148+
95149 @ Override
96150 public SkyflowClientBuilder removeVaultConfig (String vaultId ) throws SkyflowException {
97151 super .removeVaultConfig (vaultId );
@@ -110,6 +164,75 @@ public SkyflowClientBuilder setLogLevel(LogLevel logLevel) {
110164 return this ;
111165 }
112166
167+ /**
168+ * Overall call timeout in seconds, including retries. Default 60.
169+ * <p>
170+ * <b>Precedence:</b> a vault that sets {@link VaultConfig#setTimeout(Integer)} wins; this
171+ * value applies only to vaults that leave it unset.
172+ */
173+ public SkyflowClientBuilder timeout (int timeout ) {
174+ this .timeout = timeout ;
175+ propagateHttpConfig ();
176+ return this ;
177+ }
178+
179+ /**
180+ * Per-attempt connection-establishment timeout in seconds. Unset => HTTP client default (10s).
181+ * <p>
182+ * <b>Precedence:</b> a vault that sets {@link VaultConfig#setConnectTimeout(Integer)} wins;
183+ * this value applies only to vaults that leave it unset.
184+ */
185+ public SkyflowClientBuilder connectTimeout (int connectTimeout ) {
186+ this .connectTimeout = connectTimeout ;
187+ propagateHttpConfig ();
188+ return this ;
189+ }
190+
191+ /**
192+ * Per-attempt response-read timeout in seconds. Unset => HTTP client default (10s).
193+ * <p>
194+ * <b>Precedence:</b> a vault that sets {@link VaultConfig#setReadTimeout(Integer)} wins;
195+ * this value applies only to vaults that leave it unset.
196+ */
197+ public SkyflowClientBuilder readTimeout (int readTimeout ) {
198+ this .readTimeout = readTimeout ;
199+ propagateHttpConfig ();
200+ return this ;
201+ }
202+
203+ /**
204+ * Per-attempt request-write timeout in seconds. Unset => HTTP client default (10s).
205+ * <p>
206+ * <b>Precedence:</b> a vault that sets {@link VaultConfig#setWriteTimeout(Integer)} wins;
207+ * this value applies only to vaults that leave it unset.
208+ */
209+ public SkyflowClientBuilder writeTimeout (int writeTimeout ) {
210+ this .writeTimeout = writeTimeout ;
211+ propagateHttpConfig ();
212+ return this ;
213+ }
214+
215+ /**
216+ * Retry attempts after the first failure. Default 0 — retries are opt-in so non-idempotent
217+ * bulk writes are not replayed automatically.
218+ * <p>
219+ * <b>Precedence:</b> a vault that sets {@link VaultConfig#setMaxRetries(Integer)} wins;
220+ * this value applies only to vaults that leave it unset.
221+ */
222+ public SkyflowClientBuilder maxRetries (int maxRetries ) {
223+ this .maxRetries = maxRetries ;
224+ propagateHttpConfig ();
225+ return this ;
226+ }
227+
228+ /** Push the current client-wide HTTP settings onto every vault controller built so far. */
229+ private void propagateHttpConfig () {
230+ for (VaultController vault : this .vaultClientsMap .values ()) {
231+ vault .setCommonHttpConfig (this .timeout , this .connectTimeout , this .readTimeout ,
232+ this .writeTimeout , this .maxRetries );
233+ }
234+ }
235+
113236 public Skyflow build () {
114237 return new Skyflow (this );
115238 }
0 commit comments