Replies: 3 comments 1 reply
-
@borkdude some observations from me if it helps:
|
Beta Was this translation helpful? Give feedback.
-
@lispyclouds Good observations. I view http-client as a light-weight tool to make working with java.net.http easier but not as a tool which wraps Java exceptions and translates them to an |
Beta Was this translation helpful? Give feedback.
-
I think it's okay to punt the problem to the user, but at least the wrapper around Right now it feels like it does, because it obscures what exception will be raw from Also, I find it strange that it wraps http error status codes behind an exception. I'd rather get a map back with status = to whatever status code, and the rest of the keys corresponding to that status code. But if I look at https://stackoverflow.com/questions/67523816/how-where-to-check-for-java-net-http-httpclient-http-response-status-codes it seems people even find that java.net.http has badly documented error handling and struggle to handle errors with it as well. |
Beta Was this translation helpful? Give feedback.
-
In order for a user to implement a production robust use of the library APIs, the error scenarios need to be well documented, and ideally they should all follow well defined patterns so the user has a simple way to appropriately handle all error scenarios.
For example, a use case I've tried to implement is to fetch thousands of URLs, attempting for all of them to succeed.
The starting code looks like:
Where the vector of URL strings would contain thousands more URLs in it.
In a real life scenario, we can't assume we have the cleanest data, first of all that means some URLs might be malformed to begin with, such as
"garbage"
in my example.In our case,
http/get
will not behave in anasync
way even though we said:async true
, because it's URL to URI parser is actually ran synchronously and throws synchronously on error, instead of always returning a CompletableFuture.This means users must know that some errors are synchronous, while others are async. We might update the code as follows:
This delays the synchronous errors, so that they show up asynchronously later when we
deref
the results, allowing us to move all the error handling around thederef
.Next the user needs to know how to handle errors in the
deref
part. Because we want this to be a robust implementation, the user wants to perform a retry on all errors that indicate a transient issue, but would like to log and continue on all errors that indicate an issue with the input or a bug, for which retrying would be fruitless, and finally after some number of retries if the retryable ones still fail, it wants to similarly log and continue.The first code refactor might look like:
Here we're turning the deref's thrown exceptions into return values, so we can process them after to check for which one succeeded or errored.
Now we refactor the code again so we can handle errors:
This is the crux of the issue, look at the
error-type
function. Here we want a reliable way to identify everything that's not a success, and for each thing that's not a success, we want to identify if it's retryable or not retryable. But because babashka.http-client doesn't provide well defined errors, it is very difficult to know how to identify the type of errors that could happen exhaustively.Currently, it appears sometimes the error will be an
ex-info
but not always. If it is anex-info
, it's not clear how to tell what type of error it is, or if there will always be a:status
key on theex-data
for it.The user would need documented descriptions of where errors are thrown/returned, the set of all possible type of errors at each point, and their structure.
At a minimum, a consistent structure for all errors would help a lot, for example making all errors
ex-info
with the type of error documented on a:type
key on theex-data
, along with a documented list of the set of all possible types of errors, and that all errors will always have an ex-data with the details.That way inside
error-type
we could simply inspect the ex-data:type
key and if it's one of a retryable type, we can classify it as retryable, otherwise non-retryable.This would also allow us, for non-retryable ones, to know what to log in the error, since we can always expect an
ex-data
with some common structure, and aex-message
, we can log something like:Finally, it would be nice if all errors contained the URL as well which it failed on, because for the retryable ones for example, the user would want to retry, but if the error no longer contains the information to map back to which of the async
get
this was for, knowing which one to retry is non-trivial. Thus either having the URL as part of the error so we can retry using the error data itself, or having a feature to provide some ID to theasync get
call which is included on the error so we can map it back would be ideal.Regards.
Beta Was this translation helpful? Give feedback.
All reactions