-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an API compatibility check to Nessie clients #6818
Conversation
Can you move the fix for the GC tool from this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR LGTM overall. Did you have a chance to test how the new error manifests on the Iceberg side (e.g. in Spark) when the Nessie Catalog URI is misconfigured? We may need a corresponding change in the Iceberg NessieCatalog
.
// FIXME we need a better way to detect API vs URI prefix mismatches. | ||
int serverApiVersion = specVersion != null && !specVersion.isEmpty() ? 2 : 1; | ||
if (clientApiVersion != serverApiVersion) { | ||
throw new IllegalArgumentException( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT about using NessieServiceException
instead? NessieServiceException
is often reported when the URL is misconfigured in such as way that Nessie end points are not even reachable... Would it make sense to reuse it for this failure mode too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an idea, although here the error is raised on the client side. What would you say if we create a dedicated NessieApiCompatibilityException
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NessieServiceException
is also raised on the client side.
+1 to NessieApiCompatibilityException
Switching to draft, sorry, because unsurprisingly, these changes are breaking a few compatibility tests, especially |
Very good point, I will check. |
So, the compatibility tests were failing because of a combination of factors:
I temporarily fixed the issues by disabling the client compat check. But this unfortunately uncovers a deeper problem: |
OK, I am proposing something different: test what the |
|
How about returning the "effective" API version from the |
return httpClient.newRequest().path("config").get().readEntity(NessieConfiguration.class); | ||
} | ||
|
||
private static int fetchActualServerApiVersion( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check requires that authz works for you on that branch.
Also not sure whether it's really worth to run some arbitrary request as a guess implying an API version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to return the "effective" version (the one that was used on the server side) via NessieConfiguration
} | ||
|
||
private static NessieConfiguration fetchConfig(HttpClient httpClient) { | ||
return httpClient.newRequest().path("config").get().readEntity(NessieConfiguration.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'd actually have to read it into a Map<String, Object>
, because newer servers may return more elements, which would breal this check.
public static void check(int clientApiVersion, HttpClient httpClient) | ||
throws NessieApiCompatibilityException { | ||
NessieConfiguration config = fetchConfig(httpClient); | ||
int minServerApiVersion = config.getMinSupportedApiVersion(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If min/max are not present (see below), then you can safely assume V1.
If those are present, you can safely assume that V2.
No need for any other checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not true, unfortunately.
Min and max were introduced in Nessie 0.55.0, way after the introduction of v2 in 0.46.0. So all servers between [0.46.0,0.55.0[
reply with the same payload for both versions, e.g. 0.47.0:
- v1:
{"defaultBranch":"main","maxSupportedApiVersion":2}
- v2:
{"defaultBranch":"main","maxSupportedApiVersion":2}
Starting with 0.55.0, indeed the response is different:
- v1:
{"defaultBranch":"main","maxSupportedApiVersion":2}
- v2:
{"defaultBranch":"main","minSupportedApiVersion":1,"maxSupportedApiVersion":2,"specVersion":"2.0-beta.1"}
So this is not a good indicator of the actual server version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
v2 is still in beta, we can somewhat ignore older versions w/ v2.
@@ -323,12 +337,18 @@ public <API extends NessieApi> API build(Class<API> apiVersion) { | |||
if (apiVersion.isAssignableFrom(HttpApiV1.class)) { | |||
builder.setJsonView(Views.V1.class); | |||
HttpClient httpClient = builder.build(); | |||
if (enableApiCompatibilityCheck) { | |||
NessieApiCompatibility.check(1, httpClient); | |||
} | |||
return (API) new HttpApiV1(new NessieHttpClient(httpClient)); | |||
} | |||
|
|||
if (apiVersion.isAssignableFrom(HttpApiV2.class)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move this if
up (minor nit)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this would break things: HttpApiV2
is a subclass of NessieApiV1
.
@Test | ||
void testApiCompatibility() { | ||
// Good cases | ||
assertThatCode(() -> testConfig(NessieApiV1.class, 1, 1, 1, true)).doesNotThrowAnyException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Candidate for softly-assertions (soft.assertThatCode...
)
Hmm right. There will always be a risk of ambiguity it seems.
That is true as well, I didn't think of it so far. This may be a blocker.
That is imho the proper way to solve this, but keep in mind that this won't solve the problem for existing Nessie versions, only for future ones. I think this PR is getting stuck. As it stands, I see 2 options going forward:
Wdyt? |
That could work. |
...src/intTest/java/org/projectnessie/tools/compatibility/tests/AbstractCompatibilityTests.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latest approach (actual API version in NessieConfiguration
) looks good to me.
@Override | ||
void getConfig() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe rename to getConfigV1()
for clarity?
.hasMessageContaining( | ||
expectation == Expectation.MISMATCH | ||
? "mismatch" | ||
: expectation == Expectation.TOO_OLD ? "too old" : "too new") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: put too old
, etc. into enum object fields in Expectation
and use smth like expectation.getExpectedText()
... and change default URI of GC tool to a v2 URI.
Fixes #6805.