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

[WAGON-526] making the retry handle of http client configurable #37

Closed

Conversation

rmannibucau
Copy link
Contributor

No description provided.

@jkff
Copy link

jkff commented Dec 5, 2017

Very nice! Could you comment more on how to use this in practice to improve reliability w.r.t. common network issues? Is the default behavior changed?

@rmannibucau
Copy link
Contributor Author

@jkff default didnt change (backwd compat but open to suggestions)

idea is to add system properties to maven (MAVEN_OPTS). Here are some samples:

-Dmaven.wagon.http.retryhandler.class=default
-Dmaven.wagon.http.retryhandler.count=5
-Dmaven.wagon.http.retryhandler.requestSentEnabled=false #default
-Dmaven.wagon.http.retryhandler.nonRetryableClasses=javax.net.ssl.SSLException,java.net.UnknownHostException

Note that -Dmaven.wagon.http.retryhandler.class=default can be switched to -Dmaven.wagon.http.retryhandler.class=standard to use the standard retry handler impl. using a fqn class name of a http retry handler it will replace the default impl with your custom class (you must add the jar to maven libs).

@jkff
Copy link

jkff commented Dec 6, 2017

I see. So, for example, what's the shortest way to configure this to simply retry everything, including socket exceptions, host resolution exceptions etc? (what we want in Beam builds when downloading artifacts) Would it be -Dmaven.wagon.http.retryhandler.class=standard ?

@rmannibucau
Copy link
Contributor Author

I guess

-Dmaven.wagon.http.retryhandler.class=default
-Dmaven.wagon.http.retryhandler.nonRetryableClasses=java.net.UnknownHostException

can be a good start

@jkff
Copy link

jkff commented Dec 6, 2017

Wait: the default one would give current behavior, which doesn't retry on socket errors. Did you mean -Dmaven.wagon.http.retryhandler.class=standard perhaps, and without the nonRetryableClasses argument (cause I'd prefer UnknownHost to also be retried, it can happen due to transient network issues)?
It's a bit confusing that default and standard have such different meanings...

Anyway: hope that some of the maintainers can chime in perhaps?

@rmannibucau
Copy link
Contributor Author

Small misunderstanding: default behavior is the current one but default (same for standard) clasq value actually references the name of the impl -
with a shortname to be human feiendly - and default impl supports retryable exception classes configuration.

@olamy
Copy link
Member

olamy commented Dec 7, 2017

interesting feature. I wonder you could add some documentation maybe here: http://maven.apache.org/wagon/wagon-providers/wagon-http/

@rmannibucau
Copy link
Contributor Author

@olamy updated site apt

@michael-o
Copy link
Member

There is bit of a chaos now. I know that the Deploy Plugin supports something similar and people are requesting the same feature for this problem area. I'll collect and have a look at it.

@jkff
Copy link

jkff commented Jun 28, 2018

Is there anything going on with this PR? If it's stale, it might be reasonable to close it and reopen later. We're no longer even using Maven in our project so this PR is not a priority for us.

@rmannibucau
Copy link
Contributor Author

@jkff sure, this was done here to not be beam specific since it happens for multiple projects

@olamy
Copy link
Member

olamy commented Jun 29, 2018

@rmannibucau maybe you can rebase as I can see some conflicts

@michael-o
Copy link
Member

My bad, I lost completely track of this.

Copy link
Member

@michael-o michael-o left a comment

Choose a reason for hiding this comment

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

Looks quite good. Some oddities remain.

System.getProperty( "maven.wagon.http.retryhandler.class", "auto" );

/**
* true if it's OK to retry non-idempotent requests that have been sent.
Copy link
Member

Choose a reason for hiding this comment

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

What makes you think that this applies to non-idemponent methods? The HttpRequestRetryHandler doesn't even mention them.

* The type of the retry handler, default to DefaultHttpRequestRetryHandler.
* Values can be default, standard (StandardHttpRequestRetryHandler), or a fully qualified name class.
*
* @since 3.0.1
Copy link
Member

Choose a reason for hiding this comment

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

Please update this throughout to 3.2 if this one will be merged.

* @since 3.0.1
*/
private static final String RETRY_HANDLER_CLASS =
System.getProperty( "maven.wagon.http.retryhandler.class", "auto" );
Copy link
Member

Choose a reason for hiding this comment

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

This looks wrong to me. Having non-recoverable methods to be retriable by default. We have to be idiotproof and support here standard with StandardHttpRequestRetryHandler.

};
case "standard":
return new StandardHttpRequestRetryHandler( RETRY_HANDLER_COUNT, RETRY_HANDLER_REQUEST_SENT_ENABLED );
case "auto":
Copy link
Member

Choose a reason for hiding this comment

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

I don't accept this for the reasons above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changing to standard

Copy link
Member

Choose a reason for hiding this comment

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

This needs to be dropped completely.

switch ( RETRY_HANDLER_CLASS )
{
case "default":
if ( DEFAULT_NON_RETRYABLE_CLASSES.equals( RETRY_HANDLER_EXCEPTIONS ) )
Copy link
Member

Choose a reason for hiding this comment

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

This is deemed to fail if the comparison does not happen in order.

return new DefaultHttpRequestRetryHandler(
RETRY_HANDLER_COUNT, RETRY_HANDLER_REQUEST_SENT_ENABLED );
}
return new DefaultHttpRequestRetryHandler(
Copy link
Member

Choose a reason for hiding this comment

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

Is is really smart to provide something like this for unsafe methods by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed the default so guess it is ok now


* which implementation to use. `auto` will use `DefaultHttpRequestRetryHandler.INSTANCE`.
* `default` will use a new instance of `DefaultHttpRequestRetryHandler` respecting `requestSentEnabled`, `count` and `nonRetryableClasses`.
* `standard` will use an instance of `StandardHttpRequestRetryHandler` which assumes that all requested HTTP methods which should be idempotent according to RFC-2616 are in fact idempotent and can be retried. According to RFC-2616 section 9.1.2 the idempotent HTTP methods are: GET, HEAD, PUT, DELETE, OPTIONS, and TRACE. This implementation respects `requestSentEnabled` and `count`.
Copy link
Member

Choose a reason for hiding this comment

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

It's "RFC xyz". No hyphen involved.


* which implementation to use. `auto` will use `DefaultHttpRequestRetryHandler.INSTANCE`.
* `default` will use a new instance of `DefaultHttpRequestRetryHandler` respecting `requestSentEnabled`, `count` and `nonRetryableClasses`.
* `standard` will use an instance of `StandardHttpRequestRetryHandler` which assumes that all requested HTTP methods which should be idempotent according to RFC-2616 are in fact idempotent and can be retried. According to RFC-2616 section 9.1.2 the idempotent HTTP methods are: GET, HEAD, PUT, DELETE, OPTIONS, and TRACE. This implementation respects `requestSentEnabled` and `count`.
Copy link
Member

Choose a reason for hiding this comment

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

Don't repeat the same as the Javadoc of that class. Simply link to it.

@Override
public void run()
{
final HttpRequestRetryHandler handler = getCurrentHttpClient();
Copy link
Member

Choose a reason for hiding this comment

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

It is weird that getCurrentHttpClient() does not return a client actually.

{
final Field nonRetriableClasses = handler.getClass().getSuperclass()
.getDeclaredField( "nonRetriableClasses" );
if (!nonRetriableClasses.isAccessible())
Copy link
Member

Choose a reason for hiding this comment

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

Will this work with Java 10/11?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If modules are opened I think so

@rmannibucau rmannibucau force-pushed the fb/configurable-http-retry-policy branch from 6f68473 to ce7802e Compare August 20, 2018 08:59
@rmannibucau
Copy link
Contributor Author

Updated the PR accordingly the comments, let me know if it is better this way for you

Copy link
Member

@michael-o michael-o left a comment

Choose a reason for hiding this comment

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

I am still not convinced by stanard/default. From user perspective we have default and standard, what? Isn't it the same? What we have to provide has to be idiotsafe, idempotent. I currently trying to figure out what the benefit of the default is and if whether standard will cover 95% of all cases. The user still can provide a custom implementation.


[]

Since version 3.0.1, the retry handler can be configured as well with system properties:
Copy link
Member

Choose a reason for hiding this comment

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

Version seems wrong


* <<<maven.wagon.http.retryhandler.class>>> supports this set of values:

* which implementation to use. `auto` will use `DefaultHttpRequestRetryHandler.INSTANCE`.
Copy link
Member

Choose a reason for hiding this comment

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

Isn't auto gone?

* <<<maven.wagon.http.retryhandler.count>>> = number of retries for `default` or `standard` implementations.

* <<<maven.wagon.http.retryhandler.nonRetryableClasses>>> = the comma separated list of classes bypassing the retries (only the `default` implementation).
Default being `java.io.InterruptedIOException,java.io.UnknownHostException,java.io.ConnectException,java.io.SSLException`
Copy link
Member

Choose a reason for hiding this comment

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

There is no default anymore provided by us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor Author

@rmannibucau rmannibucau left a comment

Choose a reason for hiding this comment

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

Updated the doc

@michael-o
Copy link
Member

Waiting for all spots to be resolved and a JIRA issue. Will then review, test and merge.

@rmannibucau rmannibucau changed the title making the retry handle of http client configurable [WAGON-526] making the retry handle of http client configurable Aug 20, 2018
@rmannibucau
Copy link
Contributor Author

@michael-o added a jira, think I adressed the issues, please let me know if I missed some

@michael-o
Copy link
Member

You completely missed my review request:

I am still not convinced by stanard/default. From user perspective we have default and standard, what? Isn't it the same? What we have to provide has to be idiotsafe, idempotent. I currently trying to figure out what the benefit of the default is and if whether standard will cover 95% of all cases. The user still can provide a custom implementation.

@rmannibucau
Copy link
Contributor Author

@michael-o oh oki. Yes it makes sense, it basically exposes the known impl of httpclient in maven and match the same use cases. Rational was to never require a custom impl in 99% of the cases cause it is quite impacting to do it compared to configuring the standard or default impls.

@michael-o
Copy link
Member

michael-o commented Aug 20, 2018

Alright, I will have at this later this day. I think there is still room for some improvement, but I like the big picture so far.

* Comma separated list of non retryable classes.
* Note: only used for default retry handler.
*
* @since 3.0.1
Copy link
Member

Choose a reason for hiding this comment

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

Still wrong.

};
case "standard":
return new StandardHttpRequestRetryHandler( RETRY_HANDLER_COUNT, RETRY_HANDLER_REQUEST_SENT_ENABLED );
case "auto":
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be dropped completely.

@@ -354,6 +360,92 @@ private static PoolingHttpClientConnectionManager createConnManager()
return connManager;
}

/**
* The type of the retry handler, default to DefaultHttpRequestRetryHandler.
* Values can be default, standard (StandardHttpRequestRetryHandler), or a fully qualified name class.
Copy link
Member

Choose a reason for hiding this comment

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

This is inconsistent in style. Please use: label (class name), or a fully qualified class name.

System.getProperty( "maven.wagon.http.retryhandler.class", "standard" );

/**
* true if it's OK to retry non-idempotent requests that have been sent.
Copy link
Member

Choose a reason for hiding this comment

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

This statement is not true. The original documentation does not say this. Even the default implementation will stop before this value is read if the request ist not idempotent.

switch ( RETRY_HANDLER_CLASS )
{
case "default":
if ( RETRY_HANDLER_EXCEPTIONS == null )
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather rely on StringUtils#isEmpty().

final URLClassLoader loader = new URLClassLoader( urls.toArray( new URL[paths.length ] ) , new ClassLoader()
{
@Override
protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException
Copy link
Member

Choose a reason for hiding this comment

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

spaces missing

{
if ( name.startsWith( "org.apache.maven.wagon.shared.http" ) )
{
throw new ClassNotFoundException(name);
Copy link
Member

Choose a reason for hiding this comment

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

space missing

{
throw new ClassNotFoundException(name);
}
return super.loadClass(name, resolve);
Copy link
Member

Choose a reason for hiding this comment

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

space missing

});
final Thread thread = Thread.currentThread();
final ClassLoader contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(loader);
Copy link
Member

Choose a reason for hiding this comment

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

space missing

finally
{
loader.close();
thread.setContextClassLoader(contextClassLoader);
Copy link
Member

Choose a reason for hiding this comment

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

space missing

@michael-o
Copy link
Member

Looks very good now. I run push a test branch later this day.

@michael-o
Copy link
Member

I have pushed a cleaned up branch. Please have a look. If all is fine, I will merge.

@rmannibucau
Copy link
Contributor Author

@michael-o it is perfect, thanks a lot!

@asfgit asfgit closed this in f920bc1 Aug 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants