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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating SDK exceptions #301

Merged
merged 1 commit into from
Dec 13, 2017
Merged

Updating SDK exceptions #301

merged 1 commit into from
Dec 13, 2017

Conversation

spfink
Copy link
Contributor

@spfink spfink commented Nov 22, 2017

Updates the exceptions used by the SDK. Lots of files here but mostly just updating to use the new exceptions. Part 1 of additional changes around exceptions.

Description

Removes AmazonClientException, AmazonServiceException, and SdkBaseException.

Adds SdkClientException, SdkServiceException and SdkException.

SdkClientException extends SdkException
SdkServiceException extends SdkException

SdkException has a retryable() method that will allow generated exceptions to override and set this value.

Also adds NonRetryableException and RetryableException which are meant to be used by customers to explicitly mark exceptions in interceptors or lambdas.

Testing

All unit tests pass and have been updated as needed.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have read the README document
  • I have added tests to cover my changes
  • All new and existing tests passed
  • A short description of the change has been added to the CHANGELOG

License

  • I confirm that this pull request can be released under the Apache 2 license

}

private AmazonServiceException createAse(HttpResponse errorResponse) throws Exception {
private SdkServiceException createAse(HttpResponse errorResponse) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to rename this method since it's not returning AmazonServiceException anymore. createServiceException?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or createSse, that won't be ambiguous at all ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yep, will update. Thought I had most of them but found a new set. Hard to find all the cases of "ase" and all of it's casing throughout the code.

for (Unmarshaller<SdkServiceException, Node> unmarshaller : unmarshallerList) {
SdkServiceException exception = unmarshaller.unmarshall(document);
if (exception != null) {
exception.statusCode(errorResponse.getStatusCode());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could errorResponse be null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not currently aware of a real case where it could be null.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not massively expensive nor that big a maintenance burden - prolly just leave it in for now.

}

public void headers(Map<String, String> headers) {
this.headers = headers;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use unmodifiableMap here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, will update

.beginControlFlow("catch (AmazonServiceException ase)")
.beginControlFlow("if (ase.getErrorCode().equals($S) && ase.getStatusCode() == 412)",
.beginControlFlow("catch (SdkServiceException exception)")
.beginControlFlow("if (exception.errorCode().equals($S) && ase.statusCode() == 412)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ase.statusCode() -> exception.statusCode()
ase has been renamed to exception.

Similarly in line 76

@@ -15,7 +15,7 @@

package software.amazon.awssdk.codegen.protocol;

import software.amazon.awssdk.core.SdkBaseException;
import software.amazon.awssdk.core.exception.SdkServiceException;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be SdkException?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. The base exception for service exceptions is SdkServiceException

public AbortedException() {
super("");
public AbortedException(Throwable cause) {
super(null, cause);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why null here while line 31 uses "Aborted"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add it back in but this is what the prior defaults were.

Nothing -> just message "Aborted."

Message -> just message

Cause -> Just cause (message = null)

}

/**
* {@inheritDoc}
* An aborted exception is not intended to be retried.
*/
@Override
public boolean isRetryable() {
public boolean retryable() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this method here. SdkException by default returns false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

@@ -30,7 +40,7 @@ public ResetException(String message, Throwable t) {
* A stream reset exception cannot be retried.
*/
@Override
public boolean isRetryable() {
public boolean retryable() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above. can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

ase.setErrorType(AmazonServiceException.ErrorType.Service);
} else if (errorType.equalsIgnoreCase("Sender")) {
ase.setErrorType(AmazonServiceException.ErrorType.Client);
if ("Receiver".equals(errorType)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where do the values "Receiver" and "Sender"? Also document it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add documentation.

@@ -89,6 +89,10 @@ private OutputT handleSuccessResponse(HttpResponse httpResponse, RequestExecutio
} catch (IOException | InterruptedException | RetryableException e) {
throw e;
} catch (Exception e) {
if (e instanceof SdkException && ((SdkException) e).retryable()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could successResponseHandler.handle throw SdkException? I looked at some implementations but didn't find any.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could with S3.

Maybe when using lambdas as well but not sure on that.

exception.setStatusCode(statusCode);
exception.setErrorType(AmazonServiceException.ErrorType.Client);
exception.setErrorCode("Request entity too large");
SdkServiceException exception = new SdkServiceException("Request entity too large");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a SdkClientException since it's a client error? would it retry if we use SdkServiceException here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. We still pull out the status code and status text associated with the response from the service. The SdkClientException doesn't have these fields. It also doesn't portray as well that the request was made and the service rejected it as the clients fault.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually thinking this might be dead code. The error response handler should always be able to throw an unmarshalled exception no matter what happens.

@zoewangg
Copy link
Contributor

LGTM! don't forget to add changelog and good luck with merging conflicts..🙂

@spfink spfink merged commit c49d908 into master Dec 13, 2017
@shorea
Copy link
Contributor

shorea commented Dec 14, 2017

Here's my general customer focused feedback, haven't looked at the code yet.

  • We should reduce the name clashes in the base exception type. We are reserving a lot of top level names that could clash with modeled fields. One example is errorCode which clashes with Inspector and we have to customize around it. Can we push some of these down to sub-container objects to make this less of a problem.
  • Error code is definitely AWS specific and not something we should have in the base service exception class. Request ID is questionable so but it's generic enough that it could apply more broadly. I'm thinking we should have another AwsServiceException layer for things that only apply to AWS services. The current SdkServiceException can then be focused on the wire exception rather than make any kinds of protocol assumptions like error code, error type,etc.
  • Exceptions are very mutable. This is inconsistent with the generated classes which are immutable in respect to their properties. Any plans to address that? Also there are toBuilder and copy methods that just don't preserve the top level fields.
  • Headers are a map of string to string which is inconsitent with how we represent them in the SdkHttpResponse. To alleviate the name clashes and make our lives easier should we just provide access to the SdkHttpResponse object which has status code/status text, headers? We'd need to provide content separately since it's buffered
  • Have we thought about exception serialization at all? How does that work? The base classes don't have traditional getters, the builders for the generated classes have getters but don't extend from any common base.
  • The retryable method might be confusing for customers if we aren't overriding it in actual retryable exceptions. Can we hide this for now until it's more widely used in modeled exceptions?
  • Should we be returning Optional in some of the exception getters since they won't always be present?

*/
public class Crc32MismatchException extends IOException {
@SdkPublicApi
public class Crc32MismatchException extends SdkClientException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Questioning why we don't retry this. Seems like a good candidate.


/**
* Exception that is NOT retried in default retry policies.
* Extension of {@link SdkException} that can be used by clients to
* explicitly have an exception not retried. This exception will never be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last sentence here confuses me.

@@ -107,11 +107,11 @@ private RetryExecutor(SdkHttpFullRequest request, RequestExecutionContext contex
} else {
retryHandler.setLastRetriedException(handleUnmarshalledException(response));
}
} catch (AmazonServiceException e) {
} catch (SdkServiceException e) {
// TODO This can be cleaned up a bit if we have separate hierarchies for service and client exceptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we address this now?

return ase;
public SdkServiceException handle(HttpResponse response,
ExecutionAttributes executionAttributes) throws Exception {
final SdkServiceException exception = (SdkServiceException) handleServiceException(response, executionAttributes);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something we can do about the case here? If not it's not a big deal.

exception.setStatusCode(statusCode);
exception.setErrorType(AmazonServiceException.ErrorType.Client);
exception.setErrorCode("Request entity too large");
SdkServiceException exception = new SdkServiceException("Request entity too large");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually thinking this might be dead code. The error response handler should always be able to throw an unmarshalled exception no matter what happens.

exception.setStatusCode(statusCode);
exception.setErrorType(AmazonServiceException.ErrorType.Service);
exception.setErrorCode(response.getStatusText());
SdkServiceException exception = new SdkServiceException(response.getStatusText());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here I think this is dead code.

}

/**
* @deprecated In favor of {@link RetryUtils#isThrottlingException(SdkBaseException)}
* @deprecated In favor of {@link RetryUtils#isThrottlingException(SdkException)}
*/
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we handle some of these deprecations now?

return ErrorType.CLIENT;
} else {
return ErrorType.fromValue(errorType);
}
}

@ReviewBeforeRelease("We shouldn't have S3 speific code in core. Also the way this is doesn't" +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this with Zoe's change? Or has that not been merged yet?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it got removed in my PR, which got merged after this. so that's old code you are seeing 🙂

@spfink spfink mentioned this pull request Aug 8, 2018
@shorea shorea deleted the finks/exceptions branch November 23, 2018 03:53
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.

5 participants