SS-369 Refresh catalog-vended storage credentials - #38475
Conversation
b0c5679 to
cedc5d4
Compare
QA LLM Review1. MEDIUM -- Credential refresh ignores the catalog's URI override
The credential endpoint is always built from Details
2. MEDIUM -- One prefix-scoped credential is used for every table path
When DetailsThe longest-prefix rule applies after matching credentials against the path being accessed. The existing 3. MEDIUM -- A transient proactive-refresh failure discards a still-valid credential
Once the early refresh deadline is reached, Details
|
There was a problem hiding this comment.
Logic looks about right. 👍
We need some tests for this. If your Unity Catalog is always up, you should be able to point Nightly testdrives at it.
Something like what I did for GCP here (but without the materialize/i2 changes for now): a57699d
IIRC, I set the variables/secrets from i2, so you'll need to ask Dennis (or another test env owner?) to set them for you instead.
| /// others). `iceberg-rust` resolves the same value when it builds the catalog but keeps it | ||
| /// private, so this asks the server for it directly. | ||
| async fn table_credentials_endpoint( | ||
| &self, |
There was a problem hiding this comment.
table_credentials_endpoint only uses self.uri.
This function and VendedCredentialLoader could go in a new file 👀
There was a problem hiding this comment.
Also, there's enough happening in this function that we should test it. Maybe the easiest way is to split out the pure components and unit test those.
cedc5d4 to
c4f1530
Compare
c4f1530 to
166be2a
Compare
166be2a to
e400f51
Compare
QA LLM Review1. MEDIUM -- A vended credential lifetime at or under the 15-minute buffer disables the cache entirely
Details
900s is not a pathological value: it is exactly the AWS STS minimum session duration and the minimum credential duration Apache Polaris accepts, so a catalog configured for short-lived vended credentials lands on it. The degradation is also gradual and silent above that point, and it inverts the two constants: a catalog that reports no expiry at all is refreshed every A floor on the computed deadline keeps the long-lifetime behavior the buffer was chosen for while bounding the short-lifetime case, for example refreshing at |
Vended credentials expire, and nothing refreshed them: `loadTable` hands their access keys to the FileIO once and OpenDAL keeps signing with them until the dataflow restarts. Adds `VendedCredentialLoader`, a `ProvideCredential` implementation that re-fetches from the catalog's `loadCredentials` endpoint. OpenDAL rebuilds its `Operator` for every file operation, so reqsign's own credential cache never survives one call; the loader therefore caches with its own expiry deadline, taken from `s3.session-token-expires-at-ms` where the catalog reports one and a short interval where it does not. On a 401 or 403 it invalidates the catalog token so the next attempt mints a fresh one. Materialize now builds the OAuth2 provider itself rather than passing a `credential` catalog property, so one token object serves both catalog requests and credential refreshes. The catalog client rejects a custom authenticator combined with that property, so the two cannot coexist. `connect` grows a table argument because the credentials endpoint is table-scoped. Installing a loader also means it alone supplies S3 credentials, since OpenDAL replaces its whole provider chain, so one is installed only when the connection asked for delegation.
4c59283 to
1340185
Compare
Motivation
Catalog-vended credentials expire, typically within the hour, and nothing
refreshed them.
loadTablehands their access keys to the FileIO once andOpenDAL keeps signing with them until they stop working, at which point the
sink fails and only recovers by restarting the dataflow. Any sink running
longer than one credential lifetime hits this.
Description
Adds
VendedCredentialLoader, aProvideCredentialimplementation thatre-fetches from the catalog's
loadCredentialsendpoint and hands the resultto OpenDAL's S3 credential chain.
The loader caches with its own deadline rather than relying on reqsign's
cache. OpenDAL rebuilds its
Operatorfor every file operation, so theSignerthat holds reqsign's cached credential never survives a single call;without an internal cache this would be one catalog round trip per parquet
write and per metadata read. The deadline comes from
s3.session-token-expires-at-mswhere the catalog reports one, refreshingahead of expiry, and from a short fixed interval where it does not. That
interval is a constant for now, with a TODO to make it a dyncfg once we know
what real catalogs report.
On a 401 or 403 the loader invalidates the catalog token so the next attempt
mints a fresh one, since nothing else on the storage path re-mints it.
Two supporting changes:
credentialcatalog property, so a single token object serves both catalogrequests and credential refreshes. The two cannot coexist: the catalog
client rejects a custom authenticator combined with that property.
connecttakes the table the handle will be used against, because thecredentials endpoint is table-scoped. Callers that only prove reachability
(connection validation, sink purification) pass
None.Installing a loader hands it sole responsibility for S3 credentials, since
OpenDAL replaces its entire provider chain and discards the static keys
parsed from the vended properties. It is therefore installed only when the
connection asked for delegation and a table is known; every other case keeps
the existing static-property path.
Verification
cargo checkandcargo clippyare clean. Not yet exercised against a livecatalog: the refresh path needs a sink run long enough to cross a credential
lifetime, and which branch of the deadline logic applies depends on whether
the catalog reports an expiry.