Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
39e0ea4
Pulled in iceberg 1.8.0 spec changes for freshness aware table loadin…
mansehajsingh Feb 18, 2025
0ef4de8
Changed etag support to use entityId:version tuple
mansehajsingh Feb 26, 2025
bb82f56
Merge remote-tracking branch 'origin/main' into implement-freshness-a…
mansehajsingh Feb 26, 2025
44b1859
fixed getresponse call
mansehajsingh Feb 26, 2025
ab27b82
Merge remote-tracking branch 'origin/main' into implement-freshness-a…
mansehajsingh Feb 26, 2025
be3da8d
Changed etagged response to record and gave default implementation to…
mansehajsingh Feb 27, 2025
e96bbdb
Made iceberg rest spec docs clearer
mansehajsingh Feb 27, 2025
772c715
Added HTTP Compliant ETag and IfNoneMatch representations and separat…
mansehajsingh Feb 28, 2025
662f24a
Changed ETag to be a record and improved semantics of IfNoneMatch
mansehajsingh Feb 28, 2025
652cc08
Fixed semantics of if none match
mansehajsingh Feb 28, 2025
2c4fd1b
Removed ETag representation, consolidated in IfNoneMatch
mansehajsingh Mar 1, 2025
3b4e553
fixed if none match parsing
mansehajsingh Mar 4, 2025
88e75ed
Added table entity retrieval method to table operations
mansehajsingh Mar 19, 2025
c60ffa9
Merged main
mansehajsingh Mar 19, 2025
ae7e827
removed accidental commit of pycache folders
mansehajsingh Mar 19, 2025
8aba177
Merged in main and fixed conflicts with generic table support
mansehajsingh Mar 19, 2025
63c28ba
Fixed merge conflicts
mansehajsingh Mar 20, 2025
448e2d4
Fixed formatting
mansehajsingh Mar 20, 2025
8f970c4
Changed to use metadata location hash
mansehajsingh Mar 26, 2025
e8ccc76
Ran formatting
mansehajsingh Mar 26, 2025
6b25a15
use sha256
mansehajsingh Mar 26, 2025
49659f1
Moved out ETag functions to utility class and removed ETaggedLoadTabl…
mansehajsingh Mar 26, 2025
d6fd55c
Addressed comments
mansehajsingh Apr 1, 2025
b5175d7
Merge branch 'main' into implement-freshness-aware-table-loading
mansehajsingh Apr 1, 2025
6e23473
Merge branch 'main' into implement-freshness-aware-table-loading
mansehajsingh Apr 1, 2025
183b8d7
Fixed IcebergTableLikeEntity package rename
mansehajsingh Apr 1, 2025
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 @@ -26,6 +26,8 @@
import com.google.common.collect.ImmutableSet;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.SecurityContext;
Expand Down Expand Up @@ -54,6 +56,7 @@
import org.apache.iceberg.rest.requests.ReportMetricsRequest;
import org.apache.iceberg.rest.requests.UpdateNamespacePropertiesRequest;
import org.apache.iceberg.rest.responses.ConfigResponse;
import org.apache.iceberg.rest.responses.LoadTableResponse;
import org.apache.polaris.core.PolarisConfigurationStore;
import org.apache.polaris.core.PolarisDiagnostics;
import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal;
Expand Down Expand Up @@ -308,9 +311,11 @@ public Response createTable(
.build();
}
} else if (delegationModes.isEmpty()) {
return Response.ok(catalog.createTableDirect(ns, createTableRequest)).build();
LoadTableResponse loadTableResponse = catalog.createTableDirect(ns, createTableRequest);
return Response.ok(loadTableResponse).header(HttpHeaders.ETAG, loadTableResponse.metadataLocation()).build();
} else {
return Response.ok(catalog.createTableDirectWithWriteDelegation(ns, createTableRequest))
LoadTableResponse loadTableResponse = catalog.createTableDirectWithWriteDelegation(ns, createTableRequest);
return Response.ok(loadTableResponse).header(HttpHeaders.ETAG, loadTableResponse.metadataLocation())
.build();
}
});
Expand All @@ -335,6 +340,7 @@ public Response loadTable(
String namespace,
String table,
String accessDelegationMode,
String etag,
String snapshots,
RealmContext realmContext,
SecurityContext securityContext) {
Expand All @@ -347,9 +353,12 @@ public Response loadTable(
prefix,
catalog -> {
if (delegationModes.isEmpty()) {
return Response.ok(catalog.loadTable(tableIdentifier, snapshots)).build();
Optional<LoadTableResponse> optionalLoadTableResponse = catalog.loadTableFreshnessAware(tableIdentifier, etag, snapshots);
LoadTableResponse loadTableResponse = optionalLoadTableResponse.orElseThrow(() -> new WebApplicationException(Status.NOT_MODIFIED));
return Response.ok(loadTableResponse).header(HttpHeaders.ETAG, loadTableResponse.metadataLocation()).build();
} else {
return Response.ok(catalog.loadTableWithAccessDelegation(tableIdentifier, snapshots))
LoadTableResponse loadTableResponse = catalog.loadTableWithAccessDelegation(tableIdentifier, snapshots);
return Response.ok(loadTableResponse).header(HttpHeaders.ETAG, loadTableResponse.metadataLocation())
.build();
}
});
Expand Down Expand Up @@ -407,7 +416,10 @@ public Response registerTable(
return withCatalog(
securityContext,
prefix,
catalog -> Response.ok(catalog.registerTable(ns, registerTableRequest)).build());
catalog -> {
LoadTableResponse loadTableResponse = catalog.registerTable(ns, registerTableRequest);
return Response.ok(loadTableResponse).header(HttpHeaders.ETAG, loadTableResponse.metadataLocation()).build();
});
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import org.apache.polaris.core.entity.CatalogEntity;
import org.apache.polaris.core.entity.PolarisEntitySubType;
import org.apache.polaris.core.entity.PolarisEntityType;
import org.apache.polaris.core.entity.TableLikeEntity;
import org.apache.polaris.core.persistence.PolarisEntityManager;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.persistence.PolarisMetaStoreSession;
Expand Down Expand Up @@ -786,15 +787,44 @@ public boolean sendNotification(TableIdentifier identifier, NotificationRequest
&& notificationCatalog.sendNotification(identifier, request);
}

private boolean etagMatchesCurrentMetadataLocation(String etag, TableIdentifier tableIdentifier) {
Comment thread
eric-maynard marked this conversation as resolved.
Outdated
if (etag != null) {
PolarisResolvedPathWrapper target = resolutionManifest
.getResolvedPath(tableIdentifier, PolarisEntitySubType.TABLE, true);

String currentEntityMetadataVersion = target
.getRawLeafEntity()
.getInternalPropertiesAsMap()
.get(TableLikeEntity.METADATA_LOCATION_KEY);
Comment thread
eric-maynard marked this conversation as resolved.
Outdated

if (etag.equals(currentEntityMetadataVersion)) {
return true;
}
}
return false;
}

public LoadTableResponse loadTable(TableIdentifier tableIdentifier, String snapshots) {
PolarisAuthorizableOperation op = PolarisAuthorizableOperation.LOAD_TABLE;
authorizeBasicTableLikeOperationOrThrow(op, PolarisEntitySubType.TABLE, tableIdentifier);
return loadTableFreshnessAware(tableIdentifier, null, snapshots).get();
}

return CatalogHandlers.loadTable(baseCatalog, tableIdentifier);
public Optional<LoadTableResponse> loadTableFreshnessAware(TableIdentifier tableIdentifier, String etag, String snapshots) {
Comment thread
eric-maynard marked this conversation as resolved.
Outdated
PolarisAuthorizableOperation op = PolarisAuthorizableOperation.LOAD_TABLE;
authorizeBasicTableLikeOperationOrThrow(op, PolarisEntitySubType.TABLE, tableIdentifier);

if (etagMatchesCurrentMetadataLocation(etag, tableIdentifier)) {
return Optional.empty();
}

return Optional.of(CatalogHandlers.loadTable(baseCatalog, tableIdentifier));
}

public LoadTableResponse loadTableWithAccessDelegation(TableIdentifier tableIdentifier, String snapshots) {
return loadTableWithAccessDelegationFreshnessAware(tableIdentifier, null, snapshots).get();
}

public LoadTableResponse loadTableWithAccessDelegation(
TableIdentifier tableIdentifier, String snapshots) {
public Optional<LoadTableResponse> loadTableWithAccessDelegationFreshnessAware(
Comment thread
eric-maynard marked this conversation as resolved.
Outdated
TableIdentifier tableIdentifier, String etag, String snapshots) {
// Here we have a single method that falls through multiple candidate
// PolarisAuthorizableOperations because instead of identifying the desired operation up-front
// and
Expand Down Expand Up @@ -832,6 +862,10 @@ public LoadTableResponse loadTableWithAccessDelegation(
PolarisConfiguration.ALLOW_EXTERNAL_CATALOG_CREDENTIAL_VENDING.catalogConfig());
}

if (etagMatchesCurrentMetadataLocation(etag, tableIdentifier)) {
return Optional.empty();
}

// TODO: Find a way for the configuration or caller to better express whether to fail or omit
// when data-access is specified but access delegation grants are not found.
Table table = baseCatalog.loadTable(tableIdentifier);
Expand All @@ -850,7 +884,7 @@ public LoadTableResponse loadTableWithAccessDelegation(
credentialDelegation.getCredentialConfig(
tableIdentifier, tableMetadata, actionsRequested));
}
return responseBuilder.build();
return Optional.of(responseBuilder.build());
} else if (table instanceof BaseMetadataTable) {
// metadata tables are loaded on the client side, return NoSuchTableException for now
throw new NoSuchTableException("Table does not exist: %s", tableIdentifier.toString());
Expand Down
27 changes: 27 additions & 0 deletions spec/rest-catalog-open-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,15 @@ paths:
key. For example, "urn:ietf:params:oauth:token-type:jwt=<JWT-token>".
parameters:
- $ref: '#/components/parameters/data-access'
- name: If-None-Match
in: header
description:
An optional header that allows the server to return 304 (Not Modified) if the metadata
is current. The content is the value of the ETag received in a CreateTableResponse or
LoadTableResponse.
required: false
schema:
type: string
- in: query
name: snapshots
description:
Expand All @@ -698,6 +707,10 @@ paths:
responses:
200:
$ref: '#/components/responses/LoadTableResponse'
304:
description:
Not Modified - Based on the content of the 'If-None-Match' header the table metadata has
Comment thread
eric-maynard marked this conversation as resolved.
Outdated
not changed since.
400:
$ref: '#/components/responses/BadRequestErrorResponse'
401:
Expand Down Expand Up @@ -1688,6 +1701,14 @@ components:
type: integer
minimum: 1

etag:
name: ETag
in: header
description: Identifies a unique version of the table metadata.
required: false
schema:
type: string

##############################
# Application Schema Objects #
##############################
Expand Down Expand Up @@ -4193,13 +4214,19 @@ components:
application/json:
schema:
$ref: '#/components/schemas/LoadTableResult'
headers:
etag:
$ref: '#/components/parameters/etag'
Comment thread
eric-maynard marked this conversation as resolved.
Outdated

LoadTableResponse:
description: Table metadata result when loading a table
content:
application/json:
schema:
$ref: '#/components/schemas/LoadTableResult'
headers:
etag:
$ref: '#/components/parameters/etag'

LoadViewResponse:
description: View metadata result when loading a view
Expand Down