View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.ipc;
19  
20  import java.util.Deque;
21  import java.util.concurrent.BlockingQueue;
22  import java.util.concurrent.ConcurrentLinkedDeque;
23  import java.util.concurrent.atomic.AtomicInteger;
24  
25  import org.apache.hadoop.conf.Configuration;
26  import org.apache.hadoop.hbase.Abortable;
27  import org.apache.hadoop.hbase.classification.InterfaceAudience;
28  
29  /**
30   * Balanced queue executor with a fastpath. Because this is FIFO, it has no respect for
31   * ordering so a fast path skipping the queuing of Calls if an Handler is available, is possible.
32   * Just pass the Call direct to waiting Handler thread. Try to keep the hot Handlers bubbling
33   * rather than let them go cold and lose context. Idea taken from Apace Kudu (incubating). See
34   * https://gerrit.cloudera.org/#/c/2938/7/src/kudu/rpc/service_queue.h
35   */
36  @InterfaceAudience.Private
37  public class FastPathBalancedQueueRpcExecutor extends BalancedQueueRpcExecutor {
38    // Depends on default behavior of BalancedQueueRpcExecutor being FIFO!
39  
40    /*
41     * Stack of Handlers waiting for work.
42     */
43    private final Deque<FastPathRpcHandler> fastPathHandlerStack = new ConcurrentLinkedDeque<>();
44  
45    public FastPathBalancedQueueRpcExecutor(final String name, final int handlerCount,
46        final int maxQueueLength, final PriorityFunction priority, final Configuration conf,
47        final Abortable abortable) {
48      super(name, handlerCount, maxQueueLength, priority, conf, abortable);
49  
50    }
51  
52    public FastPathBalancedQueueRpcExecutor(final String name, final int handlerCount,
53        final String callQueueType, final int maxQueueLength, final PriorityFunction priority,
54        final Configuration conf, final Abortable abortable) {
55      super(name, handlerCount, callQueueType, maxQueueLength, priority, conf, abortable);
56    }
57  
58    @Override
59    protected RpcHandler getHandler(final String name, final double handlerFailureThreshhold,
60        final int handlerCount, final BlockingQueue<CallRunner> q,
61        final AtomicInteger activeHandlerCount, final AtomicInteger failedHandlerCount,
62        final Abortable abortable) {
63      return new FastPathRpcHandler(name, handlerFailureThreshhold, handlerCount, q,
64        activeHandlerCount, failedHandlerCount, abortable, fastPathHandlerStack);
65    }
66  
67    @Override
68    public boolean dispatch(CallRunner callTask) throws InterruptedException {
69      //FastPathHandlers don't check queue limits, so if we're completely shut down
70      //we have to prevent ourselves from using the handler in the first place
71      if (currentQueueLimit == 0){
72        return false;
73      }
74      FastPathRpcHandler handler = popReadyHandler();
75      return handler != null? handler.loadCallRunner(callTask): super.dispatch(callTask);
76    }
77  
78    /**
79     * @return Pop a Handler instance if one available ready-to-go or else return null.
80     */
81    private FastPathRpcHandler popReadyHandler() {
82      return this.fastPathHandlerStack.poll();
83    }
84  }