Skip to content

Propagate storage HTTP client settings to S3FileIO for table operations#5004

Merged
nandorKollar merged 3 commits into
apache:mainfrom
iprithv:fix-s3-http-client-settings
Jul 15, 2026
Merged

Propagate storage HTTP client settings to S3FileIO for table operations#5004
nandorKollar merged 3 commits into
apache:mainfrom
iprithv:fix-s3-http-client-settings

Conversation

@iprithv

@iprithv iprithv commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

polaris.storage.max-http-connections (and the related read/connect/acquisition/idle/TTL timeouts and expect-continue setting) had no effect on the HTTP clients actually used for table operations.

Users who set polaris.storage.max-http-connections=128 (plus higher polaris.tasks.max-concurrent-tasks) still observed the AWS SDK default of 50 connections. This caused connection pool exhaustion ("Timeout waiting for connection from pool") during table creation/commits, manifest cleanup, data file operations, and background tasks.

Fixes #4704

The settings were only wired into the SdkHttpClient produced for StsClientsPool (used by credential vending / AwsCredentialsStorageIntegration).

All real table I/O goes through Iceberg S3FileIO:

DefaultFileIOFactory.loadFileIOCatalogUtil.loadFileIO(..., "org.apache.iceberg.aws.s3.S3FileIO", properties) → Iceberg S3FileIO / DefaultAwsClientFactoryHttpClientProperties.applyHttpClientConfigurationsHttpClientCache → Apache HTTP client → ExceptionMappingFileIO → catalog commits / task cleanup

No http-client.apache.* properties were ever placed into the map passed to Iceberg, so it always built clients with SDK defaults (visible via Arthas as MaxConnections=50 inside the ManagedHttpClient).


// Apply polaris.storage.* HTTP client settings to Iceberg S3FileIO (and other AWS FileIOs).
// Previously only applied to the STS client pool.
if (s3AccessConfig != null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall. 👀

The null guard here has the same effect as above.. the new HTTP settings logic gets skipped for anyone using the no-arg constructor (WasbTranslatingFileIOFactory, MeasuredFileIOFactory, and the test spy all go through that path). This means the fix is really only active for the CDI-created "default" instance.

It might be worth clarifying in the code comment that this fix targets the CDI "default" path specifically - not every place FileIOFactory gets instantiated. Just helps set the right expectations and avoids someone assuming it's a global fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. updated the comments

Wire S3AccessConfig into WasbTranslatingFileIOFactory so production
non-default factories also propagate polaris.storage.* HTTP settings
to S3FileIO. Clarify that the no-arg DefaultFileIOFactory path is for
tests and fixtures only.
@iprithv
iprithv requested a review from vigneshio July 9, 2026 05:36
@vigneshio

Copy link
Copy Markdown
Contributor

LGTM.. Thanks for the quick follow-up! cc: @dimas-b

@dimas-b

dimas-b commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Are we fixing #4704 with this PR?

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing this PR, @iprithv ! I think it is an incremental improvement, but I wonder if we can take it one step further 🤔 more specific comment below.

if (s3AccessConfig != null) {
s3AccessConfig
.maxHttpConnections()
.ifPresent(v -> props.put("http-client.apache.max-connections", String.valueOf(v)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will configure the Http Client inside the FileIO with the same parameters as the Http Client used by our StsClientsPool. This is good, but it will be another Http Client instance.

However, ideally we should probably reuse the same client that is produced by sdkHttpClient().

Would that be possible?

Cf.

public SdkHttpClient sdkHttpClient(S3AccessConfig config) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible, but not via properties alone — Iceberg only configures HTTP clients from http-client.apache.* strings; it does not accept an external SdkHttpClient.

Reusing the singleton would need a custom AwsClientFactory (or custom S3 client supplier) wired to ServiceProducers.sdkHttpClient(), plus lifecycle care so FileIO close does not dispose the shared client (httpClient(instance) is caller-owned).

Trade-off: STS and S3 FileIO would then compete for one connection pool under load. This PR keeps separate pools with the same polaris.storage.* tuning (Iceberg already shares among FileIOs via HttpClientCache). Suggest treating full reuse as a follow-up unless you prefer expanding this PR.

@iprithv iprithv Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for checking! on reusing sdkHttpClient(), Iceberg’s FileIO path only takes http-client.apache.* properties, so we can’t pass the existing client through...sharing would need a custom AwsClientFactory (or S3 client supplier) wired to the singleton, plus care so FileIO close doesn’t dispose it.
also STS and S3 FileIO would then share one connection pool and could contend under load.

here the same polaris.storage.* settings to both I did, but kept separate clients (Iceberg still shares among FileIOs via HttpClientCache).

happy to follow up on true reuse. wdyt?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Thanks for the info 👍

@iprithv

iprithv commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Are we fixing #4704 with this PR?

Yes, this is intended to fix #4704 :)

@iprithv
iprithv force-pushed the fix-s3-http-client-settings branch from db64ba2 to f5b1118 Compare July 10, 2026 20:32

// Get subcoped creds
properties = new HashMap<>(properties);
Map<String, String> props = new HashMap<>(properties);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather rename the method parameter as opposed to the local var to minimize non-functional changes below.

Why do we need to use a different variable for this anyway?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, renamed the parameter to tableProperties and kept the local as properties so the rest of the method stays the same.

// Apply polaris.storage.* HTTP client settings to Iceberg S3FileIO (and other AWS FileIOs).
// Previously only applied to the STS client pool. Skipped when constructed without config
// (tests / fixtures); production CDI paths always inject S3AccessConfig.
if (s3AccessConfig != null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be easy to add @PolarisImmutable to S3AccessConfig and create a default (empty) instance from it to avoid null config.

Only one constructor should be sufficient for all use cases, I guess... WDYT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, makes sense.. added @PolarisImmutable on S3AccessConfig with S3AccessConfig.empty(), dropped the no-arg constructor, and always take a config.. tests/fixtures use S3AccessConfig.empty() (empty optionals → no HTTP props applied)

s3AccessConfig
.readTimeout()
.ifPresent(
d -> props.put("http-client.apache.socket-timeout-ms", String.valueOf(d.toMillis())));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reuse property names from Iceberg's HttpClientProperties?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, done. thanks!

Address review feedback: use S3AccessConfig.empty() via @PolarisImmutable
instead of null, rename loadFileIO tableProperties param, and reuse Iceberg
HttpClientProperties constants. Keep regenerated polaris.storage config docs
in the same commit for CI.
@iprithv
iprithv force-pushed the fix-s3-http-client-settings branch from f5b1118 to 6f0fd02 Compare July 10, 2026 20:58
@iprithv
iprithv requested a review from dimas-b July 10, 2026 21:24

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @iprithv !

@github-project-automation github-project-automation Bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Jul 10, 2026
@dimas-b

dimas-b commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Targeting Jul 13 for the merge date, unless fresh comments appear.

@nandorKollar nandorKollar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the change looks good, my only concern is that now S3-specific configs are leaking into the otherwise storage-agnostic DefaultFileIOFactory. I doubt that this causes real-word problems, though look a bit awkward. Are these configs applied now on Azure and GCP storage too? What happens, when S3 is not used at all?

@dimas-b

dimas-b commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Overall the change looks good, my only concern is that now S3-specific configs are leaking into the otherwise storage-agnostic DefaultFileIOFactory

I noticed that too. My reading that such a leak existed before too, but was realized in untyped FileIO property bags. This PR merely makes it more visible by using a strongly typed config class. All in all, I think it's fine for now.

@dimas-b

dimas-b commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What happens, when S3 is not used at all?

The S3 config will be "empty". It is ultimately produced by Quarkus at the server start time (once per instance) and should not cause too much overhead.

@nandorKollar

Copy link
Copy Markdown
Contributor

Overall the change looks good, my only concern is that now S3-specific configs are leaking into the otherwise storage-agnostic DefaultFileIOFactory

I noticed that too. My reading that such a leak existed before too, but was realized in untyped FileIO property bags. This PR merely makes it more visible by using a strongly typed config class. All in all, I think it's fine for now.

Thanks @dimas-b for the explanation. Agree, this is fine for now.

@nandorKollar

Copy link
Copy Markdown
Contributor

@iprithv would you mind resolve the merge conflict on the changelog?

@iprithv

iprithv commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Is it only for me? I don't see any conflict on this 😅

Screenshot 2026-07-15 at 1 24 09 AM

@nandorKollar
nandorKollar merged commit 26a33dc into apache:main Jul 15, 2026
25 checks passed
@github-project-automation github-project-automation Bot moved this from Ready to merge to Done in Basic Kanban Board Jul 15, 2026
@dimas-b

dimas-b commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

I guess someone changed CHANGELOG on main at the conflict was gone 😉

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.

maxHttpConnections of S3AccessConfig not work

4 participants