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

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


The following commit(s) were added to refs/heads/master by this push:
     new 95ebc45  ISSUE #209: Introduce Speculative Read/Read LAC policy
95ebc45 is described below

commit 95ebc45fa2d0c4028c31c66b790743f4a7909800
Author: Sijie Guo <[email protected]>
AuthorDate: Thu Jun 29 15:08:17 2017 -0700

    ISSUE #209: Introduce Speculative Read/Read LAC policy
    
    Descriptions of the changes in this PR:
    
        - Separate out the logic that determines when speculative reads are 
scheduled from the individual requests into a separate 
SpeculativeRequestExecutionPolicy
        - Implement the default speculative read execution policy based on the 
current mechanism of using first and max timeout
        - Initialize the policy once in the BookKeeper object and use it for 
individual requests by keeping information about the requests in the call chain.
    
    ---
    Be sure to do all of the following to help us incorporate your contribution
    quickly and easily:
    
    - [x] Make sure the PR title is formatted like:
        `<Issue #>: Description of pull request`
        `e.g. Issue 123: Description ...`
    - [x] Make sure tests pass via `mvn clean apache-rat:check install 
findbugs:check`.
    - [x] Replace `<Issue #>` in the title with the actual Issue number, if 
there is one.
    
    ---
    
    Author: Sijie Guo <[email protected]>
    Author: Robin Dhamankar <[email protected]>
    Author: Sijie Guo <[email protected]>
    
    Reviewers: Enrico Olivelli <[email protected]>, Jia Zhai <None>
    
    This closes #200 from sijie/client_changes/speculative_policy, closes #209
---
 .../org/apache/bookkeeper/client/BookKeeper.java   |  27 ++++--
 .../DefaultSpeculativeRequestExecutionPolicy.java  | 102 +++++++++++++++++++++
 .../apache/bookkeeper/client/PendingReadOp.java    |  69 ++++++--------
 .../client/SpeculativeRequestExectuor.java         |  34 +++++++
 .../client/SpeculativeRequestExecutionPolicy.java  |  34 +++++++
 .../bookkeeper/conf/ClientConfiguration.java       |  66 +++++++++++++
 .../bookkeeper/util/OrderedSafeExecutor.java       |   1 +
 7 files changed, 286 insertions(+), 47 deletions(-)

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java
index 9a09084..2d509d4 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java
@@ -21,6 +21,9 @@
 package org.apache.bookkeeper.client;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.epoll.EpollEventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
@@ -29,6 +32,7 @@ import io.netty.util.HashedWheelTimer;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -36,9 +40,6 @@ import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
 import org.apache.bookkeeper.client.AsyncCallback.CreateCallback;
 import org.apache.bookkeeper.client.AsyncCallback.DeleteCallback;
 import org.apache.bookkeeper.client.AsyncCallback.OpenCallback;
@@ -70,9 +71,6 @@ import org.apache.zookeeper.ZooKeeper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-
-import java.util.concurrent.CompletableFuture;
 
 /**
  * BookKeeper client. We assume there is one single writer to a ledger at any
@@ -86,7 +84,6 @@ import java.util.concurrent.CompletableFuture;
  *
  *
  */
-
 public class BookKeeper implements AutoCloseable {
 
     static final Logger LOG = LoggerFactory.getLogger(BookKeeper.class);
@@ -136,6 +133,8 @@ public class BookKeeper implements AutoCloseable {
     final ClientConfiguration conf;
     final int explicitLacInterval;
 
+    final Optional<SpeculativeRequestExecutionPolicy> 
readSpeculativeRequestPolicy;
+
     // Close State
     boolean closed = false;
     final ReentrantReadWriteLock closeLock = new ReentrantReadWriteLock();
@@ -356,6 +355,16 @@ public class BookKeeper implements AutoCloseable {
         this.placementPolicy = initializeEnsemblePlacementPolicy(conf,
                 dnsResolver, this.requestTimer, this.featureProvider, 
this.statsLogger);
 
+        if (conf.getFirstSpeculativeReadTimeout() > 0) {
+            this.readSpeculativeRequestPolicy =
+                    Optional.of(new DefaultSpeculativeRequestExecutionPolicy(
+                        conf.getFirstSpeculativeReadTimeout(),
+                        conf.getMaxSpeculativeReadTimeout(),
+                        conf.getSpeculativeReadTimeoutBackoffMultiplier()));
+        } else {
+            this.readSpeculativeRequestPolicy = 
Optional.<SpeculativeRequestExecutionPolicy>absent();
+        }
+
         // initialize main worker pool
         this.mainWorkerPool = OrderedSafeExecutor.newBuilder()
                 .name("BookKeeperClientWorker")
@@ -481,6 +490,10 @@ public class BookKeeper implements AutoCloseable {
         return statsLogger;
     }
 
+    public Optional<SpeculativeRequestExecutionPolicy> 
getReadSpeculativeRequestPolicy() {
+        return readSpeculativeRequestPolicy;
+    }
+
     /**
      * Get the BookieClient, currently used for doing bookie recovery.
      *
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultSpeculativeRequestExecutionPolicy.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultSpeculativeRequestExecutionPolicy.java
new file mode 100644
index 0000000..d0a3672
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultSpeculativeRequestExecutionPolicy.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.bookkeeper.client;
+
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultSpeculativeRequestExecutionPolicy implements 
SpeculativeRequestExecutionPolicy {
+    private static final Logger LOG = 
LoggerFactory.getLogger(PendingReadOp.class);
+    final int firstSpeculativeRequestTimeout;
+    final int maxSpeculativeRequestTimeout;
+    final float backoffMultiplier;
+
+    public DefaultSpeculativeRequestExecutionPolicy(int 
firstSpeculativeRequestTimeout, int maxSpeculativeRequestTimeout, float 
backoffMultiplier) {
+        this.firstSpeculativeRequestTimeout = firstSpeculativeRequestTimeout;
+        this.maxSpeculativeRequestTimeout = maxSpeculativeRequestTimeout;
+        this.backoffMultiplier = backoffMultiplier;
+
+        if (backoffMultiplier <= 0) {
+            throw new IllegalArgumentException("Invalid value provided for 
backoffMultiplier");
+        }
+
+        // Prevent potential over flow
+        if (Math.round((double)maxSpeculativeRequestTimeout * 
(double)backoffMultiplier) > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("Invalid values for 
maxSpeculativeRequestTimeout and backoffMultiplier");
+        }
+    }
+
+    /**
+     * Initialize the speculative request execution policy
+     *
+     * @param scheduler The scheduler service to issue the speculative request
+     * @param requestExecutor The executor is used to issue the actual 
speculative requests
+     */
+    @Override
+    public void initiateSpeculativeRequest(final ScheduledExecutorService 
scheduler, final SpeculativeRequestExectuor requestExecutor) {
+        scheduleSpeculativeRead(scheduler, requestExecutor, 
firstSpeculativeRequestTimeout);
+    }
+
+    private void scheduleSpeculativeRead(final ScheduledExecutorService 
scheduler,
+                                         final SpeculativeRequestExectuor 
requestExecutor,
+                                         final int speculativeRequestTimeout) {
+        try {
+            scheduler.schedule(new Runnable() {
+                @Override
+                public void run() {
+                    ListenableFuture<Boolean> issueNextRequest = 
requestExecutor.issueSpeculativeRequest();
+                    Futures.addCallback(issueNextRequest, new 
FutureCallback<Boolean>() {
+                        // we want this handler to run immediately after we 
push the big red button!
+                        public void onSuccess(Boolean issueNextRequest) {
+                            if (issueNextRequest) {
+                                scheduleSpeculativeRead(scheduler, 
requestExecutor, Math.min(maxSpeculativeRequestTimeout,
+                                    
Math.round((float)speculativeRequestTimeout * backoffMultiplier)));
+                            } else {
+                                if(LOG.isTraceEnabled()) {
+                                    LOG.trace("Stopped issuing speculative 
requests for {}, " +
+                                        "speculativeReadTimeout = {}", 
requestExecutor, speculativeRequestTimeout);
+                                }
+                            }
+                        }
+
+                        public void onFailure(Throwable thrown) {
+                            LOG.warn("Failed to issue speculative request for 
{}, speculativeReadTimeout = {} : ",
+                                new Object[] { requestExecutor, 
speculativeRequestTimeout, thrown });
+                        }
+                    });
+                }
+            }, speculativeRequestTimeout, TimeUnit.MILLISECONDS);
+        } catch (RejectedExecutionException re) {
+            if (!scheduler.isShutdown()) {
+                LOG.warn("Failed to schedule speculative request for {}, 
speculativeReadTimeout = {} : ",
+                        new Object[]{requestExecutor, 
speculativeRequestTimeout, re});
+            }
+        }
+    }
+}
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/PendingReadOp.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/PendingReadOp.java
index 0dd1f67..f2477c1 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/PendingReadOp.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/PendingReadOp.java
@@ -31,12 +31,14 @@ import java.util.NoSuchElementException;
 import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import org.apache.bookkeeper.client.AsyncCallback.ReadCallback;
 import org.apache.bookkeeper.client.BKException.BKDigestMatchException;
 import org.apache.bookkeeper.net.BookieSocketAddress;
@@ -57,7 +59,6 @@ import org.slf4j.LoggerFactory;
 class PendingReadOp implements Enumeration<LedgerEntry>, ReadEntryCallback {
     private static final Logger LOG = 
LoggerFactory.getLogger(PendingReadOp.class);
 
-    final int speculativeReadTimeout;
     final private ScheduledExecutorService scheduler;
     private ScheduledFuture<?> speculativeTask = null;
     Queue<LedgerEntryRequest> seq;
@@ -76,7 +77,7 @@ class PendingReadOp implements Enumeration<LedgerEntry>, 
ReadEntryCallback {
     boolean parallelRead = false;
     final AtomicBoolean complete = new AtomicBoolean(false);
 
-    abstract class LedgerEntryRequest extends LedgerEntry {
+    abstract class LedgerEntryRequest extends LedgerEntry implements 
SpeculativeRequestExectuor {
 
         final AtomicBoolean complete = new AtomicBoolean(false);
 
@@ -226,6 +227,28 @@ class PendingReadOp implements Enumeration<LedgerEntry>, 
ReadEntryCallback {
             return String.format("L%d-E%d", ledgerId, entryId);
         }
 
+        /**
+         * Issues a speculative request and indicates if more speculative
+         * requests should be issued
+         *
+         * @return whether more speculative requests should be issued
+         */
+        @Override
+        public ListenableFuture<Boolean> issueSpeculativeRequest() {
+            return lh.bk.mainWorkerPool.submitOrdered(lh.getId(), new 
Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    if (!isComplete() && null != 
maybeSendSpeculativeRead(heardFromHostsBitSet)) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Send speculative read for {}. Hosts 
heard are {}, ensemble is {}.",
+                                new Object[] { this, heardFromHostsBitSet, 
ensemble });
+                        }
+                        return true;
+                    }
+                    return false;
+                }
+            });
+        }
     }
 
     class ParallelReadRequest extends LedgerEntryRequest {
@@ -402,7 +425,6 @@ class PendingReadOp implements Enumeration<LedgerEntry>, 
ReadEntryCallback {
         numPendingEntries = endEntryId - startEntryId + 1;
         maxMissedReadsAllowed = getLedgerMetadata().getWriteQuorumSize()
                 - getLedgerMetadata().getAckQuorumSize();
-        speculativeReadTimeout = lh.bk.getConf().getSpeculativeReadTimeout();
         heardFromHosts = new HashSet<>();
         heardFromHostsBitSet = new 
BitSet(getLedgerMetadata().getEnsembleSize());
 
@@ -430,42 +452,6 @@ class PendingReadOp implements Enumeration<LedgerEntry>, 
ReadEntryCallback {
         this.requestTimeNanos = MathUtils.nowInNano();
         ArrayList<BookieSocketAddress> ensemble = null;
 
-        if (speculativeReadTimeout > 0 && !parallelRead) {
-            Runnable readTask = new Runnable() {
-                public void run() {
-                    int x = 0;
-                    for (LedgerEntryRequest r : seq) {
-                        if (!r.isComplete()) {
-                            if (null == 
r.maybeSendSpeculativeRead(heardFromHostsBitSet)) {
-                                // Subsequent speculative read will not 
materialize anyway
-                                cancelSpeculativeTask(false);
-                            } else {
-                                if (LOG.isDebugEnabled()) {
-                                    LOG.debug("Send speculative read for {}. 
Hosts heard are {}.", r, heardFromHostsBitSet);
-                                }
-                                ++x;
-                            }
-                        }
-                    }
-                    if (x > 0) {
-                        if (LOG.isDebugEnabled()) {
-                            LOG.debug("Send {} speculative reads for ledger {} 
({}, {}). Hosts heard are {}.",
-                                    new Object[] { x, lh.getId(), 
startEntryId, endEntryId, heardFromHostsBitSet });
-                        }
-                    }
-                }
-            };
-            try {
-                speculativeTask = scheduler.scheduleWithFixedDelay(readTask,
-                        speculativeReadTimeout, speculativeReadTimeout, 
TimeUnit.MILLISECONDS);
-            } catch (RejectedExecutionException re) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("Failed to schedule speculative reads for ledger 
{} ({}, {}) : ", lh.getId(),
-                            startEntryId, endEntryId, re);
-                }
-            }
-        }
-
         do {
             if (i == nextEnsembleChange) {
                 ensemble = getLedgerMetadata().getEnsemble(i);
@@ -483,6 +469,9 @@ class PendingReadOp implements Enumeration<LedgerEntry>, 
ReadEntryCallback {
         // read the entries.
         for (LedgerEntryRequest entry : seq) {
             entry.read();
+            if (!parallelRead && 
lh.bk.getReadSpeculativeRequestPolicy().isPresent()) {
+                
lh.bk.getReadSpeculativeRequestPolicy().get().initiateSpeculativeRequest(scheduler,
 entry);
+            }
         }
     }
 
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/SpeculativeRequestExectuor.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/SpeculativeRequestExectuor.java
new file mode 100644
index 0000000..45e1aeb
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/SpeculativeRequestExectuor.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.bookkeeper.client;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface SpeculativeRequestExectuor {
+
+    /**
+     * Issues a speculative request and indicates if more speculative
+     * requests should be issued
+     *
+     * @return whether more speculative requests should be issued
+     */
+    ListenableFuture<Boolean> issueSpeculativeRequest();
+}
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/SpeculativeRequestExecutionPolicy.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/SpeculativeRequestExecutionPolicy.java
new file mode 100644
index 0000000..4489d41
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/SpeculativeRequestExecutionPolicy.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.bookkeeper.client;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+public interface SpeculativeRequestExecutionPolicy {
+
+    /**
+     * Initialize the speculative request execution policy and initiate 
requests
+     *
+     * @param scheduler The scheduler service to issue the speculative request
+     * @param requestExectuor The executor is used to issue the actual 
speculative requests
+     */
+    void initiateSpeculativeRequest(ScheduledExecutorService scheduler, 
SpeculativeRequestExectuor requestExectuor);
+}
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ClientConfiguration.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ClientConfiguration.java
index 8b02a80..32b21aa 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ClientConfiguration.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ClientConfiguration.java
@@ -62,6 +62,9 @@ public class ClientConfiguration extends 
AbstractConfiguration {
     // Read Parameters
     protected final static String READ_TIMEOUT = "readTimeout";
     protected final static String SPECULATIVE_READ_TIMEOUT = 
"speculativeReadTimeout";
+    protected final static String FIRST_SPECULATIVE_READ_TIMEOUT = 
"firstSpeculativeReadTimeout";
+    protected final static String MAX_SPECULATIVE_READ_TIMEOUT = 
"maxSpeculativeReadTimeout";
+    protected final static String SPECULATIVE_READ_TIMEOUT_BACKOFF_MULTIPLIER 
= "speculativeReadTimeoutBackoffMultiplier";
     protected final static String ENABLE_PARALLEL_RECOVERY_READ = 
"enableParallelRecoveryRead";
     protected final static String RECOVERY_READ_BATCH_SIZE = 
"recoveryReadBatchSize";
     // Timeout Setting
@@ -795,6 +798,69 @@ public class ClientConfiguration extends 
AbstractConfiguration {
     }
 
     /**
+     * Get the first speculative read timeout.
+     *
+     * @return first speculative read timeout.
+     */
+    public int getFirstSpeculativeReadTimeout() {
+        return getInt(FIRST_SPECULATIVE_READ_TIMEOUT, 
getSpeculativeReadTimeout());
+    }
+
+    /**
+     * Set the first speculative read timeout.
+     *
+     * @param timeout
+     *          first speculative read timeout.
+     * @return client configuration.
+     */
+    public ClientConfiguration setFirstSpeculativeReadTimeout(int timeout) {
+        setProperty(FIRST_SPECULATIVE_READ_TIMEOUT, timeout);
+        return this;
+    }
+
+    /**
+     * Multipler to use when determining time between successive speculative 
read requests
+     *
+     * @return speculative read timeout backoff multiplier.
+     */
+    public float getSpeculativeReadTimeoutBackoffMultiplier() {
+        return getFloat(SPECULATIVE_READ_TIMEOUT_BACKOFF_MULTIPLIER, 2.0f);
+    }
+
+    /**
+     * Set the multipler to use when determining time between successive 
speculative read requests
+     *
+     * @param speculativeReadTimeoutBackoffMultiplier
+     *          multipler to use when determining time between successive 
speculative read requests.
+     * @return client configuration.
+     */
+    public ClientConfiguration 
setSpeculativeReadTimeoutBackoffMultiplier(float 
speculativeReadTimeoutBackoffMultiplier) {
+        setProperty(SPECULATIVE_READ_TIMEOUT_BACKOFF_MULTIPLIER, 
speculativeReadTimeoutBackoffMultiplier);
+        return this;
+    }
+
+    /**
+     * Get the max speculative read timeout.
+     *
+     * @return max speculative read timeout.
+     */
+    public int getMaxSpeculativeReadTimeout() {
+        return getInt(MAX_SPECULATIVE_READ_TIMEOUT, 
getSpeculativeReadTimeout());
+    }
+
+    /**
+     * Set the max speculative read timeout.
+     *
+     * @param timeout
+     *          max speculative read timeout.
+     * @return client configuration.
+     */
+    public ClientConfiguration setMaxSpeculativeReadTimeout(int timeout) {
+        setProperty(MAX_SPECULATIVE_READ_TIMEOUT, timeout);
+        return this;
+    }
+
+    /**
      * Whether to enable parallel reading in recovery read.
      *
      * @return true if enable parallel reading in recovery read. otherwise, 
return false.
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java
index c3554b4..76f0830 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java
@@ -246,6 +246,7 @@ public class OrderedSafeExecutor {
     }
 
     public ListeningScheduledExecutorService chooseThread(Object orderingKey) {
+        // skip hashcode generation in this special case
         if (threads.length == 1) {
             return threads[0];
         }

-- 
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].

Reply via email to