Skip to content

Parse Iceberg client language and version from request headers - #4236

Closed
adutra wants to merge 1 commit into
apache:mainfrom
adutra:iceberg-client-info
Closed

Parse Iceberg client language and version from request headers#4236
adutra wants to merge 1 commit into
apache:mainfrom
adutra:iceberg-client-info

Conversation

@adutra

@adutra adutra commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

This change introduces a new IcebergClientInfo component to identify the Iceberg client library making each REST request.

This information may be useful not only for logging and tracing, but also for future enhancements such as when deciding:

  • How to decode path segments (the decoding algorithm is likely to change)
  • How to process a remote signing request (the spec is likely to evolve)

The information is sourced from two HTTP headers:

  • X-Client-Version: carries the client version string for Java and Python.
  • User-Agent: carries the version for Python, Go, Rust, and C++.

The client info is propagated to:

  • OpenTelemetry spans via TracingFilter
  • SLF4J MDC via LoggingMDCFilter

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes #
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how)
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

@flyrain flyrain 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 @adutra for adding this. It's a very useful feature to me. Left some minor comments.

public interface IcebergClientInfo {

/** Programming language of an Iceberg REST client, as determined from request headers. */
enum Language {

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.

Do we need an enum here? I think a string would be more flexible when clients provides a lang not in this list.

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.

The language is inferred, not provided. So I think an enum is fine.


/** Returns the Iceberg client library version, or empty if it cannot be determined. */
@Value.Parameter(order = 1)
Optional<String> icebergVersion();

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.

Would client version a more descriptive name? icebergVersion may be confused with table versions.

Not a blocker, just brain storming: should we add a field called client name? For example, PyIceberg is the client name, 0.9.1 is the client version.

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.

Basically, we could have 3 field Client language, Client name, and Client version. 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.

Not opposed to that, but wouldn't client name be a bit redundant with client language? I.e. PyIceberg vs PYTHON.

@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.

Looks like a useful feature to me 👍

This change introduces a new `IcebergClientInfo` component to identify the Iceberg client library making each REST request.

This information may be useful not only for logging and tracing, but also for future enhancements such as when deciding:

- How to decode path segments (the decoding algorithm is likely to change)
- How to process a remote signing request (the spec is likely to evolve)

The information is sourced from two HTTP headers:

- `X-Client-Version`: carries the client version string for Java and Python.
- `User-Agent`: carries the version for Python, Go, Rust, and C++.

The client info is propagated to:

- OpenTelemetry spans via `TracingFilter`
- SLF4J MDC via `LoggingMDCFilter`
@adutra
adutra force-pushed the iceberg-client-info branch from c3e612d to b6d1cc4 Compare April 18, 2026 10:57
}
}

// 2. Parse User-Agent for other clients, which (except for Python) send a stale, hardcoded

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.

+1 on the logic here. I think User-Agent is more reliable, except for Java clients which don't currently include Iceberg version metadata there. This leads to two implications::

  1. Given X-Client-Version isn't part of IRC spec. It is a convention only working for Java clients, we can change the Iceberg Java clients to send back Iceberg version informations via header User-Agent, so that the iceberg clients across language behavior are more consistent. However, we still have to keep this logic in Polaris to be compatible with the old clients until they are irrelevant.
  2. Alternative option: Make X-Client-Version part of the IRC, so that other clients need to follow it.
Language X-Client-Version User-Agent Version Extraction Strategy Implementation Status
Java Apache Iceberg 1.10.1 (commit abc123...) Apache-HttpClient/5.4 (Java/11.0.25) Parse X-Client-Version ⚠️ User-Agent not helpful
Python PyIceberg 0.9.1 PyIceberg/0.9.1 Parse User-Agent (X-Client-Version also works) ✅ Consistent
Go 0.14.1 GoIceberg/0.5.0 Parse User-Agent ⚠️ X-Client-Version is spec version
Rust 0.14.1 iceberg-rs/0.3.0 Parse User-Agent ⚠️ X-Client-Version is spec version
C++ (not sent) iceberg-cpp/0.3.0-SNAPSHOT Parse User-Agent ⚠️ Missing X-Client-Version


} else if (userAgentHeader.startsWith("iceberg-rs/")) {
// Rust: "iceberg-rs/<version>"
int start = "iceberg-rs/".length();

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 don't think language inferring is reliable. The Iceberg rust clients may change it to Iceberg-rs or following the go's convention RustIceberg. I'd suggest to keep the client name as is. I'm also concerned the version inferring, which isn't necessary reliable, what if next rust/go version starts to adopt the Java client information format Apache Iceberg 1.10.1 (commit abc123...). Do we have strong use case for client versions? If not, I'd suggest to just keep as is in Polaris. Then we leave the parsing logic to the downstream.

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.

Do we have strong use case for client versions?

Not now, but maybe one day e.g. in order to spot Java clients using the wrong percent-encoding to encode URL path segments (see apache/iceberg#15989).

The Iceberg rust clients may change it to Iceberg-rs or following the go's convention RustIceberg

While they could do this, I don't think they would. Changing the format of a well-established User-Agent header is a delicate operation.

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.

And to complete my answer: a component like this is necessarily brittle, and can only operate on a best-effort basis. If clients change their headers, this component will need to be updated accordingly.

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'm hesitant to rely on brittle inference for any feature. If Polaris ever need to depend on some information from client-side infor. to switch logic. I'd prefer explicit ways, like spec changes or headers.

@singhpk234

Copy link
Copy Markdown
Contributor

This is really nice change @adutra thank you for adding this !

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions Bot added the stale label May 23, 2026
@github-actions github-actions Bot closed this May 28, 2026
@github-project-automation github-project-automation Bot moved this from PRs In Progress to Done in Basic Kanban Board May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants