You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2023/08/31 07:37:10 UTC

[httpcomponents-client] branch master updated: HTTPCLIENT-2291: fixed inconsistency in behavior between the class and async implementation of the request re-execution. The async request retry exec will now re-start request execution from itself instead of form the very beginning of the execution chain

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a54e7035 HTTPCLIENT-2291: fixed inconsistency in behavior between the class and async implementation of the request re-execution. The async request retry exec will now re-start request execution from itself instead of form the very beginning of the execution chain
8a54e7035 is described below

commit 8a54e70359dd511956728d3ca073ce94f71ac729
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Aug 30 19:41:11 2023 +0200

    HTTPCLIENT-2291: fixed inconsistency in behavior between the class and async implementation of the request re-execution. The async request retry exec will now re-start request execution from itself instead of form the very beginning of the execution chain
---
 .../hc/client5/http/async/AsyncExecChain.java      | 28 +++++++++++++++-
 .../http/impl/async/AsyncHttpRequestRetryExec.java | 16 +++++++--
 .../async/InternalAbstractHttpAsyncClient.java     | 39 +++++++++++++++++++---
 3 files changed, 75 insertions(+), 8 deletions(-)

diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecChain.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecChain.java
index 081946029..2ba4c192d 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecChain.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/AsyncExecChain.java
@@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.concurrent.CancellableDependency;
 import org.apache.hc.core5.http.HttpException;
@@ -116,7 +117,7 @@ public interface AsyncExecChain {
          * @param request the actual request.
          * @param entityProducer the request entity producer or {@code null} if the request
          *                      does not enclose an entity.
-         * @param scope the execution scope .
+         * @param scope the execution scope.
          * @param asyncExecCallback the execution callback.
          * @param delay re-execution delay. Can be {@code null} if the request is to be
          *              re-executed immediately.
@@ -128,6 +129,31 @@ public interface AsyncExecChain {
                 AsyncExecCallback asyncExecCallback,
                 TimeValue delay);
 
+        /**
+         * Schedules request re-execution of the given execution chain immediately or
+         * after a delay.
+         * @param request the actual request.
+         * @param entityProducer the request entity producer or {@code null} if the request
+         *                      does not enclose an entity.
+         * @param scope the execution scope.
+         * @param chain the execution chain.
+         * @param asyncExecCallback the execution callback.
+         * @param delay re-execution delay. Can be {@code null} if the request is to be
+         *              re-executed immediately.
+         *
+         * @since 5.3
+         */
+        @Internal
+        default void scheduleExecution(
+                HttpRequest request,
+                AsyncEntityProducer entityProducer,
+                AsyncExecChain.Scope scope,
+                AsyncExecChain chain,
+                AsyncExecCallback asyncExecCallback,
+                TimeValue delay) {
+            scheduleExecution(request, entityProducer, scope, asyncExecCallback, delay);
+        }
+
     }
 
     /**
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncHttpRequestRetryExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncHttpRequestRetryExec.java
index a38bfd42f..9fc7c3296 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncHttpRequestRetryExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncHttpRequestRetryExec.java
@@ -131,7 +131,13 @@ public final class AsyncHttpRequestRetryExec implements AsyncExecChainHandler {
                     if (entityProducer != null) {
                        entityProducer.releaseResources();
                     }
-                    scope.scheduler.scheduleExecution(request, entityProducer, scope, asyncExecCallback, state.delay);
+                    scope.scheduler.scheduleExecution(
+                            request,
+                            entityProducer,
+                            scope,
+                            (r, e, s, c) -> execute(r, e, s, chain, c),
+                            asyncExecCallback,
+                            state.delay);
                 } else {
                     asyncExecCallback.completed();
                 }
@@ -161,7 +167,13 @@ public final class AsyncHttpRequestRetryExec implements AsyncExecChainHandler {
                         state.retrying = true;
                         final int execCount = scope.execCount.incrementAndGet();
                         state.delay = retryStrategy.getRetryInterval(request, (IOException) cause, execCount - 1, clientContext);
-                        scope.scheduler.scheduleExecution(request, entityProducer, scope, asyncExecCallback, state.delay);
+                        scope.scheduler.scheduleExecution(
+                                request,
+                                entityProducer,
+                                scope,
+                                (r, e, s, c) -> execute(r, e, s, chain, c),
+                                asyncExecCallback,
+                                state.delay);
                         return;
                     }
                 }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java
index 4e9a7379b..277437062 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java
@@ -92,6 +92,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
     private final RequestConfig defaultConfig;
     private final ConcurrentLinkedQueue<Closeable> closeables;
     private final ScheduledExecutorService scheduledExecutorService;
+    private final AsyncExecChain.Scheduler scheduler;
 
     InternalAbstractHttpAsyncClient(
             final DefaultConnectingIOReactor ioReactor,
@@ -113,6 +114,30 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
         this.defaultConfig = defaultConfig;
         this.closeables = closeables != null ? new ConcurrentLinkedQueue<>(closeables) : null;
         this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(SCHEDULER_THREAD_FACTORY);
+        this.scheduler = new AsyncExecChain.Scheduler() {
+
+            @Override
+            public void scheduleExecution(
+                    final HttpRequest request,
+                    final AsyncEntityProducer entityProducer,
+                    final AsyncExecChain.Scope scope,
+                    final AsyncExecCallback asyncExecCallback,
+                    final TimeValue delay) {
+                executeScheduled(request, entityProducer, scope, execChain::execute, asyncExecCallback, delay);
+            }
+
+            @Override
+            public void scheduleExecution(
+                    final HttpRequest request,
+                    final AsyncEntityProducer entityProducer,
+                    final AsyncExecChain.Scope scope,
+                    final AsyncExecChain chain,
+                    final AsyncExecCallback asyncExecCallback,
+                    final TimeValue delay) {
+                executeScheduled(request, entityProducer, scope, chain, asyncExecCallback, delay);
+            }
+        };
+
     }
 
     @Override
@@ -197,8 +222,6 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
                 }
                 final AsyncExecRuntime execRuntime = createAsyncExecRuntime(pushHandlerFactory);
 
-                final AsyncExecChain.Scheduler scheduler = this::executeScheduled;
-
                 final AsyncExecChain.Scope scope = new AsyncExecChain.Scope(exchangeId, route, request, future,
                         clientContext, execRuntime, scheduler, new AtomicInteger(1));
                 final AtomicBoolean outputTerminated = new AtomicBoolean(false);
@@ -262,6 +285,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
 
                         } : null,
                         scope,
+                        execChain::execute,
                         new AsyncExecCallback() {
 
                             @Override
@@ -343,18 +367,20 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
             final HttpRequest request,
             final AsyncEntityProducer entityProducer,
             final AsyncExecChain.Scope scope,
+            final AsyncExecChain chain,
             final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
-        execChain.execute(request, entityProducer, scope, asyncExecCallback);
+        chain.proceed(request, entityProducer, scope, asyncExecCallback);
     }
 
     void executeScheduled(
             final HttpRequest request,
             final AsyncEntityProducer entityProducer,
             final AsyncExecChain.Scope scope,
+            final AsyncExecChain chain,
             final AsyncExecCallback asyncExecCallback,
             final TimeValue delay) {
         final ScheduledRequestExecution scheduledTask = new ScheduledRequestExecution(
-                request, entityProducer, scope, asyncExecCallback, delay);
+                request, entityProducer, scope, chain, asyncExecCallback, delay);
         if (TimeValue.isPositive(delay)) {
             scheduledExecutorService.schedule(scheduledTask, delay.getDuration(), delay.getTimeUnit());
         } else {
@@ -367,17 +393,20 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
         final HttpRequest request;
         final AsyncEntityProducer entityProducer;
         final AsyncExecChain.Scope scope;
+        final AsyncExecChain chain;
         final AsyncExecCallback asyncExecCallback;
         final TimeValue delay;
 
         ScheduledRequestExecution(final HttpRequest request,
                                   final AsyncEntityProducer entityProducer,
                                   final AsyncExecChain.Scope scope,
+                                  final AsyncExecChain chain,
                                   final AsyncExecCallback asyncExecCallback,
                                   final TimeValue delay) {
             this.request = request;
             this.entityProducer = entityProducer;
             this.scope = scope;
+            this.chain = chain;
             this.asyncExecCallback = asyncExecCallback;
             this.delay = delay;
         }
@@ -385,7 +414,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
         @Override
         public void run() {
             try {
-                execChain.execute(request, entityProducer, scope, asyncExecCallback);
+                chain.proceed(request, entityProducer, scope, asyncExecCallback);
             } catch (final Exception ex) {
                 asyncExecCallback.failed(ex);
             }