Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better api error handling #62

Closed
wants to merge 4 commits into from

Conversation

StefMa
Copy link
Contributor

@StefMa StefMa commented Feb 20, 2024

All I wanted to fix is #47 .. suddently it ended up in a short rewrite 馃槄
fixes #47

Main fix is here:
Instead of throwing a GradleException in case API return != 200 status code, we return an Result.failure (Kotlin builtin).
This is how it look before this PR:

if (status != HttpURLConnection.HTTP_OK) {
throw GradleException("Requesting vendor list failed: ${readContent(con.errorStream)}")
}

Now it looks like this

        if (status != HttpURLConnection.HTTP_OK) {
            return Result.failure(GradleException("Requesting vendor list failed: ${readContent(con.errorStream)}"))
        }

The result will be bubbled up until it reaches the Resolver.
The Resolver will then do a logging (warn level) and return an Optional.empty.

The resulting message will then instead of one of the linked issue be like:

* What went wrong:
A problem occurred configuring project ':domain:map'.
> Failed to calculate the value of task ':domain:map:compileJava' property 'javaCompiler'.
   > Cannot find a Java installation on your machine matching this tasks requirements: {languageVersion=18, vendor=AZUL, implementation=vendor-specific} for MAC_OS on aarch64.
      > No locally installed toolchains match and the configured toolchain download repositories aren't able to provide a match either.

(Ignore the case that a java 18 Zulu package is available, for testing purpose I modified the result.success case to be an error 馃槈 )

This is the "default" error message that will be displayed by the Gradle build tool itself and I think it is more obvious (but more generic) than the one before.

Furthermore, if running with --info (or something) the output includes Failed to resolve Java toolchain.
Not sure if we want to enhance this? 馃
We can 馃し 馃榿

This doesn't "fix the problem" in case of foojay api is down AND there is no local jdk installed.
In this case users are "locked in" and can't work until foojay api is up again.
But I guess providing a fallback is out of scope of this plugin ...
Maybe we can enhance the error message with "If foojay api is down, use one of the locally installed java versions instead: [listOfLocallyAvailableJdks]"

What do you think? 馃

Whatever, about the rewrite.. 馃榿
I extracted a FoojayService out of FoojayApi.
FoojayApi has only the responsibility to make the connection and return an Result<JsonString>.
Thats it.
FoojayService will handle the findMatchingDistribution, followed by the findMatchingPackag logic...
I have the feeling this makes things a bit nicer ...

Signed-off-by: StefMa <StefMaDev@outlook.com>
Signed-off-by: StefMa <StefMaDev@outlook.com>
Signed-off-by: StefMa <StefMaDev@outlook.com>
Signed-off-by: StefMa <StefMaDev@outlook.com>
@jbartok
Copy link
Member

jbartok commented Feb 21, 2024

Thanks @StefMa. I keep wondering if it wouldn't be a better fix to catch the original exception on the Gradle side and provide a better error message... Because with or without your changes, the build has to fail; it can't really continue (it attempted to download a toolchain because there is no local alternative). I will get a second opinion internally.

@StefMa
Copy link
Contributor Author

StefMa commented Feb 21, 2024

Currently it will fail.
But not with the Exception error message thrown by the foojay-toolchain plugin , but with the Gradle error message

No locally installed toolchains match and the configured toolchain download repositories aren't able to provide a match either.

And I guess this is the right approach.
Because, in fact, the user don't have locally installed toolchains and the toolchain repositores aren't able to provide a match either (in the reported issue case because the API returns status code).

So this behavior is... correct?!

Why should Gradle itself catch an exception in case an toolchain repository throws an error?
In case a toolchain repo can't provide a matching URL (for whatever reasons, backend down, requested jdk is not available anymore, ... 馃し ) it should return an Optional.empty.

I think Gradle shouldn't catch exceptions happen in that toolchain repos because it also don't catch exceptions in (normal) plugins. They will also be just thrown.

Just my two cents on that 馃檭

@jbartok
Copy link
Member

jbartok commented Feb 23, 2024

@StefMa after thinking a bit more about it, I propose we do the following:

  1. NOT do this change to the plugin
  2. Do this change on the Gradle side instead
  3. Optionally, if you are interested, add some exponential backoff type of retry mechanism to the plugin, to reduce the number of failures

I think the Gradle side change is necessary, because, if one resolver fails, then we still want to try subsequent ones, if they have been set.

This fix will also offer a solution to build authors: they will now be able to set multiple resolvers (assuming the community writes some more) to have a backup when Foojay backend failures happen.

WDYT?

@StefMa
Copy link
Contributor Author

StefMa commented Feb 23, 2024

Hey @jbartok,

I guess the propsed change on Gradle side makes a lot of sense.
As you mentioned, throwing of one toolchain provider shouldn't prevent Gradle to check additional registered tolchain providers 馃憤

I also think that adding a retry mechanism to this plugin makes sense.
Just two or three times should be enough to fetch some hiccups.

However, I also think it make sense to still add this fix to the plugin. Even though Gradle will catch exceptions, the goal of a toolchain provider should be to provide an Optional.empty in case it can't provide a toolchain URI. Other toolchain providers, if there any 馃榿 , shouldn't think that, taking this plugin as a reference, throwing in the middle of the process is a "good"/"accepted"/"fine" pattern.
This plugin throw a exception by intention. I guess this shouldn't be the case.
As long as toolchain provider authors can catch/avoiding throwing exceptions, they should doing that.

I guess the fix here and the fix in Gradle are separated things.
The fix in Gradle is needed in case a toolchain provider does a mistake and throws for "edge cases"...

Both fixes strengthen the toolchain support in Gradle.
But I guess both are desired to work separated and intepnend from each other... 馃

@jbartok
Copy link
Member

jbartok commented Feb 23, 2024

I'm not sure we want to blur the distinction between "Fojjay has no matching toolchains in their repertoire" and "Foojay has crashed and burned". Will think about it.

@jbartok
Copy link
Member

jbartok commented Feb 27, 2024

@StefMa, your reasoning makes sense to me, and I'm not 100% sure of myself when I say this, but let's go with JUST the Gradle side fix for now. We can always revisit this later if we change our minds.

I will open a separate issue for the exponential back-off improvement to the plugin, in case somebody decides to implement it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Better handling for broken connection to Foojay backend
2 participants