REST: Freshness-aware loading breaks when the server writes the ETag header in lowercase
- Dominant language
- Java
- Stars
- 9.2k
- Forks
- 3.5k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 132
Description
### Apache Iceberg version
1.11.0 (latest release)
### Query engine
None
### Please describe the bug
Freshness-aware table loading silently does nothing when the catalog server writes the `ETag` response header in any spelling other than `ETag`.
`HTTPClient` hands response headers to callers in a map keyed by the name exactly as it came off the wire:
```java
// core/src/main/java/org/apache/iceberg/rest/HTTPClient.java
Map respHeaders = Maps.newHashMap();
for (Header header : response.getHeaders()) {
respHeaders.put(header.getName(), header.getValue());
}
responseHeaders.accept(respHeaders);
```
`RESTSessionCatalog` copies that into another `HashMap` and reads the tag back by its traditional spelling:
```java
// core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
String eTag = responseHeaders.getOrDefault(HttpHeaders.ETAG, null); // "ETag"
if (eTag != null) {
tableCache.put(context.sessionId(), finalIdentifier, tableSupplier, eTag);
}
```
HTTP field names are case-insensitive (RFC 9110 section 5.1), and HTTP/2 requires them to be lowercase (RFC 9113 section 8.2.1), so the server's chosen spelling is not something a client may key on. When a server sends `etag`, the lookup misses, `tableCache` is never populated, no later load sends `If-None-Match`, and the whole mechanism is inert -- with no error and nothing in the logs to suggest the server did answer with a tag.
This is not hypothetical. It is what happens against an Armeria-based catalog server, which writes HTTP/1 header names in their lowercase HTTP/2 form by default; I hit it while adding conditional `loadTable` support to Unity Catalog's Iceberg REST endpoints, where the server was returning a correct `ETag` that the client could not see. It also means the feature cannot work over HTTP/2 against any server, since lowercase names are mandatory there.
The fix is small: expose the response headers in a case-insensitive map, in `HTTPClient` where they are collected and in `RESTSessionCatalog` where they are copied.
### Willingness to contribute
- [x] I can contribute a fix for this bug independently
Contributor guide
Research direction
Start in core/src/main/java/org/apache/iceberg/rest/HTTPClient.java at response header collection, then inspect the header copy and ETag lookup in core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java. Verify that lowercase and traditionally cased ETag headers both populate tableCache and allow later loads to use If-None-Match.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100