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

gaul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jclouds.git


The following commit(s) were added to refs/heads/master by this push:
     new 1c57d07  JCLOUDS-847: Poor upload performance for putBlob
1c57d07 is described below

commit 1c57d07f707be26cafc9d0da9abdc82af2827ad8
Author: Dori Polotsky <[email protected]>
AuthorDate: Mon Apr 22 22:55:09 2019 +0300

    JCLOUDS-847: Poor upload performance for putBlob
    
    This change improves the performance of writing to sockets with the
    default Java URL connection HTTP client, by enlarging the buffer used
    for socket writes from an implicit hard-coded 4KB / 8KB buffer to a
    configurable 32KB buffer.
    
    The buffer size is now controlled by the following property with the
    following default value:
    
    jclouds.output-socket-buffer-size: 32768
    
    The implementation is based on a variant of ByteStreams.copy (written as
    ByteStreams2.copy) which accepts the buffer size as an argument, unlike
    the original Guava code that uses a hard-coded size.
    
    The change was done directly within the loop that copies the input
    stream to the output stream, and not by wrapping a BufferedOutputStream
    around the existing output stream, in order to avoid copying the payload
    twice.
    
    On some platforms this change can improve both the putBlob throughput
    and the total CPU consumption.
---
 core/src/main/java/org/jclouds/Constants.java      | 10 +++++++++-
 .../org/jclouds/apis/internal/BaseApiMetadata.java |  2 ++
 .../JavaUrlHttpCommandExecutorService.java         |  8 ++++++--
 .../src/main/java/org/jclouds/io/ByteStreams2.java | 23 ++++++++++++++++++++++
 .../TrackingJavaUrlHttpCommandExecutorService.java |  4 +++-
 .../dynect/v3/config/DynECTHttpApiModule.java      |  4 +++-
 ...tatusFromPayloadHttpCommandExecutorService.java |  4 +++-
 7 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/core/src/main/java/org/jclouds/Constants.java 
b/core/src/main/java/org/jclouds/Constants.java
index 388cc25..6c2143f 100644
--- a/core/src/main/java/org/jclouds/Constants.java
+++ b/core/src/main/java/org/jclouds/Constants.java
@@ -325,7 +325,15 @@ public final class Constants {
     * </code>
     */
    public static final String PROPERTY_TIMEOUTS_PREFIX = "jclouds.timeouts.";
-   
+
+   /**
+    * Integer property. Default (32768).
+    * <p/>
+    * Buffer size for socket write (currently honored only by the default
+    * Java URL HTTP client, JavaUrlHttpCommandExcecutorService).
+    */
+   public static final String PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE = 
"jclouds.output-socket-buffer-size";
+
    /**
     * Boolean property. Default (true).
     * <p/>
diff --git a/core/src/main/java/org/jclouds/apis/internal/BaseApiMetadata.java 
b/core/src/main/java/org/jclouds/apis/internal/BaseApiMetadata.java
index 5b85dc7..9202530 100644
--- a/core/src/main/java/org/jclouds/apis/internal/BaseApiMetadata.java
+++ b/core/src/main/java/org/jclouds/apis/internal/BaseApiMetadata.java
@@ -27,6 +27,7 @@ import static 
org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST;
 import static org.jclouds.Constants.PROPERTY_MAX_CONNECTION_REUSE;
 import static org.jclouds.Constants.PROPERTY_MAX_PARALLEL_DELETES;
 import static org.jclouds.Constants.PROPERTY_MAX_SESSION_FAILURES;
+import static org.jclouds.Constants.PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE;
 import static org.jclouds.Constants.PROPERTY_PRETTY_PRINT_PAYLOADS;
 import static org.jclouds.Constants.PROPERTY_SCHEDULER_THREADS;
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
@@ -89,6 +90,7 @@ public abstract class BaseApiMetadata implements ApiMetadata {
       props.setProperty(PROPERTY_MAX_PARALLEL_DELETES, numUserThreads + "");
 
       props.setProperty(PROPERTY_IDEMPOTENT_METHODS, 
"DELETE,GET,HEAD,OPTIONS,PUT");
+      props.setProperty(PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE, 32768 + "");
       return props;
    }
 
diff --git 
a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
 
b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
index f64f4a7..1e455e2 100644
--- 
a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
+++ 
b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
@@ -22,6 +22,7 @@ import static 
com.google.common.net.HttpHeaders.CONTENT_LENGTH;
 import static com.google.common.net.HttpHeaders.HOST;
 import static com.google.common.net.HttpHeaders.USER_AGENT;
 import static org.jclouds.Constants.PROPERTY_IDEMPOTENT_METHODS;
+import static org.jclouds.Constants.PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE;
 import static org.jclouds.Constants.PROPERTY_USER_AGENT;
 import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
 import static org.jclouds.io.Payloads.newInputStreamPayload;
@@ -50,6 +51,7 @@ import org.jclouds.http.HttpUtils;
 import org.jclouds.http.IOExceptionRetryHandler;
 import org.jclouds.http.handlers.DelegatingErrorHandler;
 import org.jclouds.http.handlers.DelegatingRetryHandler;
+import org.jclouds.io.ByteStreams2;
 import org.jclouds.io.ContentMetadataCodec;
 import org.jclouds.io.MutableContentMetadata;
 import org.jclouds.io.Payload;
@@ -58,7 +60,6 @@ import com.google.common.base.Function;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableMultimap.Builder;
-import com.google.common.io.ByteStreams;
 import com.google.common.io.CountingOutputStream;
 import com.google.inject.Inject;
 
@@ -69,6 +70,7 @@ public class JavaUrlHttpCommandExecutorService extends 
BaseHttpCommandExecutorSe
    protected final HostnameVerifier verifier;
    @Inject(optional = true)
    protected Supplier<SSLContext> sslContextSupplier;
+   protected final int outputSocketBufferSize;
    protected final String userAgent;
 
    @Inject
@@ -77,6 +79,7 @@ public class JavaUrlHttpCommandExecutorService extends 
BaseHttpCommandExecutorSe
          DelegatingErrorHandler errorHandler, HttpWire wire, 
@Named("untrusted") HostnameVerifier verifier,
          @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, 
Function<URI, Proxy> proxyForURI,
          @Named(PROPERTY_IDEMPOTENT_METHODS) String idempotentMethods,
+         @Named(PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE) int outputSocketBufferSize,
          @Named(PROPERTY_USER_AGENT) String userAgent) {
       super(utils, contentMetadataCodec, retryHandler, ioRetryHandler, 
errorHandler, wire, idempotentMethods);
       if (utils.getMaxConnections() > 0) {
@@ -86,6 +89,7 @@ public class JavaUrlHttpCommandExecutorService extends 
BaseHttpCommandExecutorSe
       this.verifier = checkNotNull(verifier, "verifier");
       this.proxyForURI = checkNotNull(proxyForURI, "proxyForURI");
       this.userAgent = userAgent;
+      this.outputSocketBufferSize = outputSocketBufferSize;
    }
 
    @Override
@@ -295,7 +299,7 @@ public class JavaUrlHttpCommandExecutorService extends 
BaseHttpCommandExecutorSe
       CountingOutputStream out = new 
CountingOutputStream(connection.getOutputStream());
       InputStream is = payload.openStream();
       try {
-         ByteStreams.copy(is, out);
+         ByteStreams2.copy(is, out, outputSocketBufferSize);
       } catch (IOException e) {
          logger.error(e, "error after writing %d/%s bytes to %s", 
out.getCount(), lengthDesc, connection.getURL());
          throw e;
diff --git a/core/src/main/java/org/jclouds/io/ByteStreams2.java 
b/core/src/main/java/org/jclouds/io/ByteStreams2.java
index 814f1ec..44bd818 100644
--- a/core/src/main/java/org/jclouds/io/ByteStreams2.java
+++ b/core/src/main/java/org/jclouds/io/ByteStreams2.java
@@ -17,11 +17,13 @@
 
 package org.jclouds.io;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.util.Closeables2.closeQuietly;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 
 import com.google.common.annotations.Beta;
 import com.google.common.hash.HashCode;
@@ -31,6 +33,8 @@ import com.google.common.io.ByteStreams;
 
 @Beta
 public class ByteStreams2 {
+   private static final int INPUT_STREAM_READ_END_OF_STREAM_INDICATOR = -1;
+
    public static HashCode hashAndClose(InputStream input, HashFunction 
hashFunction) throws IOException {
       checkNotNull(input, "input");
       checkNotNull(hashFunction, "hashFunction");
@@ -51,4 +55,23 @@ public class ByteStreams2 {
          closeQuietly(input);
       }
    }
+
+   public static long copy(InputStream from, OutputStream to, int bufferSize) 
throws IOException {
+      checkNotNull(from, "from");
+      checkNotNull(to, "to");
+      checkArgument(bufferSize >= 1, "bufferSize must be >= 1");
+
+      byte[] buf = new byte[bufferSize];
+      long total = 0L;
+
+      while (true) {
+         int len = from.read(buf);
+         if (len == INPUT_STREAM_READ_END_OF_STREAM_INDICATOR) {
+            return total;
+         }
+
+         to.write(buf, 0, len);
+         total += (long)len;
+      }
+   }
 }
diff --git 
a/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
 
b/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
index 35fe0ef..5b82765 100644
--- 
a/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
+++ 
b/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
@@ -17,6 +17,7 @@
 package org.jclouds.http.internal;
 
 import static org.jclouds.Constants.PROPERTY_IDEMPOTENT_METHODS;
+import static org.jclouds.Constants.PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE;
 import static org.jclouds.Constants.PROPERTY_USER_AGENT;
 
 import java.net.Proxy;
@@ -92,10 +93,11 @@ public class TrackingJavaUrlHttpCommandExecutorService 
extends JavaUrlHttpComman
             @Named("untrusted") Supplier<SSLContext> 
untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI,
             List<HttpCommand> commandsInvoked,
             @Named(PROPERTY_IDEMPOTENT_METHODS) String idempotentMethods,
+            @Named(PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE) int 
outputSocketBufferSize,
             @Named(PROPERTY_USER_AGENT) String userAgent)
             throws SecurityException, NoSuchFieldException {
       super(utils, contentMetadataCodec, retryHandler, ioRetryHandler, 
errorHandler, wire, verifier,
-            untrustedSSLContextProvider, proxyForURI, idempotentMethods, 
userAgent);
+            untrustedSSLContextProvider, proxyForURI, idempotentMethods, 
outputSocketBufferSize, userAgent);
       this.commandsInvoked = commandsInvoked;
    }
 
diff --git 
a/providers/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTHttpApiModule.java
 
b/providers/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTHttpApiModule.java
index a4ca194..5074b80 100644
--- 
a/providers/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTHttpApiModule.java
+++ 
b/providers/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTHttpApiModule.java
@@ -17,6 +17,7 @@
 package org.jclouds.dynect.v3.config;
 
 import static org.jclouds.Constants.PROPERTY_IDEMPOTENT_METHODS;
+import static org.jclouds.Constants.PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE;
 import static org.jclouds.Constants.PROPERTY_USER_AGENT;
 import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
 import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
@@ -106,10 +107,11 @@ public class DynECTHttpApiModule extends 
HttpApiModule<DynECTApi> {
             DelegatingErrorHandler errorHandler, HttpWire wire, 
@Named("untrusted") HostnameVerifier verifier,
             @Named("untrusted") Supplier<SSLContext> 
untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI,
             @Named(PROPERTY_IDEMPOTENT_METHODS) String idempotentMethods,
+            @Named(PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE) int 
outputSocketBufferSize,
             @Named(PROPERTY_USER_AGENT) String userAgent)
             throws SecurityException, NoSuchFieldException {
          super(utils, contentMetadataCodec, retryHandler, ioRetryHandler, 
errorHandler, wire, verifier,
-               untrustedSSLContextProvider, proxyForURI, idempotentMethods, 
userAgent);
+               untrustedSSLContextProvider, proxyForURI, idempotentMethods, 
outputSocketBufferSize, userAgent);
       }
 
       /**
diff --git 
a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
 
b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
index 4e337ff..afca237 100644
--- 
a/providers/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
+++ 
b/providers/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
@@ -17,6 +17,7 @@
 package org.jclouds.profitbricks.http;
 
 import static org.jclouds.Constants.PROPERTY_IDEMPOTENT_METHODS;
+import static org.jclouds.Constants.PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE;
 import static org.jclouds.Constants.PROPERTY_USER_AGENT;
 import static org.jclouds.util.Closeables2.closeQuietly;
 
@@ -71,9 +72,10 @@ public class 
ResponseStatusFromPayloadHttpCommandExecutorService extends JavaUrl
            @Named("untrusted") Supplier<SSLContext> 
untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI,
            ParseSax<ServiceFault> faultHandler,
            @Named(PROPERTY_IDEMPOTENT_METHODS) String idempotentMethods,
+           @Named(PROPERTY_OUTPUT_SOCKET_BUFFER_SIZE) int 
outputSocketBufferSize,
            @Named(PROPERTY_USER_AGENT) String userAgent) {
       super(utils, contentMetadataCodec, retryHandler, ioRetryHandler, 
errorHandler, wire, verifier, untrustedSSLContextProvider, proxyForURI,
-            idempotentMethods, userAgent);
+            idempotentMethods, outputSocketBufferSize, userAgent);
       this.faultHandler = faultHandler;
    }
 

Reply via email to