You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by te...@apache.org on 2013/01/30 16:10:41 UTC

svn commit: r1440454 [3/3] - in /hbase/trunk: hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/ hbase-protocol/src/main/protobuf/ hbase-server/src/main/java/org/apache/hadoop/hbase/client/ hbase-server/src/main/java/org/apache/ha...

Modified: hbase/trunk/hbase-protocol/src/main/protobuf/Client.proto
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-protocol/src/main/protobuf/Client.proto?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-protocol/src/main/protobuf/Client.proto (original)
+++ hbase/trunk/hbase-protocol/src/main/protobuf/Client.proto Wed Jan 30 15:10:40 2013
@@ -71,8 +71,22 @@ message GetRequest {
   optional bool closestRowBefore = 3;
 
   // The result isn't asked for, just check for
-  // the existence. If specified, closestRowBefore
-  // will be ignored
+  // the existence. If closestRowBefore specified,
+  // this will be ignored
+  optional bool existenceOnly = 4;
+}
+
+message MultiGetRequest {
+  required RegionSpecifier region = 1;
+  repeated Get get = 2;
+
+  // If the row to get doesn't exist, return the
+  // closest row before.
+  optional bool closestRowBefore = 3;
+
+  // The result isn't asked for, just check for
+  // the existence. If closestRowBefore specified,
+  // this will be ignored
   optional bool existenceOnly = 4;
 }
 
@@ -83,6 +97,13 @@ message GetResponse {
   optional bool exists = 2;
 }
 
+message MultiGetResponse {
+  repeated Result result = 1;
+
+  // used for Get to check existence only
+  repeated bool exists = 2;
+}
+
 /**
  * Condition to check if the value of a given cell (row,
  * family, qualifier) matches a value via a given comparator.
@@ -299,6 +320,9 @@ service ClientService {
   rpc get(GetRequest)
     returns(GetResponse);
 
+  rpc multiGet(MultiGetRequest)
+    returns(MultiGetResponse);
+
   rpc mutate(MutateRequest)
     returns(MutateResponse);
 

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTable.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTable.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTable.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTable.java Wed Jan 30 15:10:40 2013
@@ -24,6 +24,7 @@ import java.io.InterruptedIOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
@@ -57,6 +58,8 @@ import org.apache.hadoop.hbase.protobuf.
 import org.apache.hadoop.hbase.protobuf.RequestConverter;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetResponse;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetRequest;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetResponse;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateResponse;
@@ -872,6 +875,147 @@ public class HTable implements HTableInt
   }
 
   /**
+   * Goal of this inner class is to keep track of the initial position of a get in a list before
+   * sorting it. This is used to send back results in the same orders we got the Gets before we sort
+   * them.
+   */
+  private static class SortedGet implements Comparable<SortedGet> {
+    protected int initialIndex = -1; // Used to store the get initial index in a list.
+    protected Get get; // Encapsulated Get instance.
+
+    public SortedGet (Get get, int initialIndex) {
+      this.get = get;
+      this.initialIndex = initialIndex;
+    }
+
+    public int getInitialIndex() {
+      return initialIndex;
+    }
+
+    @Override
+    public int compareTo(SortedGet o) {
+      return get.compareTo(o.get);
+    }
+
+    public Get getGet() {
+      return get;
+    }
+
+    @Override
+    public int hashCode() {
+      return get.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj instanceof SortedGet)
+        return get.equals(((SortedGet)obj).get);
+      else
+        return false;
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Boolean[] exists(final List<Get> gets) throws IOException {
+    // Prepare the sorted list of gets. Take the list of gets received, and encapsulate them into
+    // a list of SortedGet instances. Simple list parsing, so complexity here is O(n)
+    // The list is later used to recreate the response order based on the order the Gets
+    // got received.
+    ArrayList<SortedGet> sortedGetsList = new ArrayList<HTable.SortedGet>();
+    for (int indexGet = 0; indexGet < gets.size(); indexGet++) {
+      sortedGetsList.add(new SortedGet (gets.get(indexGet), indexGet));
+    }
+
+    // Sorting the list to get the Gets ordered based on the key.
+    Collections.sort(sortedGetsList); // O(n log n)
+
+    // step 1: sort the requests by regions to send them bundled.
+    // Map key is startKey index. Map value is the list of Gets related to the region starting
+    // with the startKey.
+    Map<Integer, List<Get>> getsByRegion = new HashMap<Integer, List<Get>>();
+
+    // Reference map to quickly find back in which region a get belongs.
+    Map<Get, Integer> getToRegionIndexMap = new HashMap<Get, Integer>();
+    Pair<byte[][], byte[][]> startEndKeys = getStartEndKeys();
+
+    int regionIndex = 0;
+    for (final SortedGet get : sortedGetsList) {
+      // Progress on the regions until we find the one the current get resides in.
+      while ((regionIndex < startEndKeys.getSecond().length) && ((Bytes.compareTo(startEndKeys.getSecond()[regionIndex], get.getGet().getRow()) <= 0))) {
+        regionIndex++;
+      }
+      List<Get> regionGets = getsByRegion.get(regionIndex);
+      if (regionGets == null) {
+        regionGets = new ArrayList<Get>();
+        getsByRegion.put(regionIndex, regionGets);
+      }
+      regionGets.add(get.getGet());
+      getToRegionIndexMap.put(get.getGet(), regionIndex);
+    }
+
+    // step 2: make the requests
+    Map<Integer, Future<List<Boolean>>> futures =
+        new HashMap<Integer, Future<List<Boolean>>>(sortedGetsList.size());
+    for (final Map.Entry<Integer, List<Get>> getsByRegionEntry : getsByRegion.entrySet()) {
+      Callable<List<Boolean>> callable = new Callable<List<Boolean>>() {
+        public List<Boolean> call() throws Exception {
+          return new ServerCallable<List<Boolean>>(connection, tableName, getsByRegionEntry.getValue()
+              .get(0).getRow(), operationTimeout) {
+            public List<Boolean> call() throws IOException {
+              try {
+                MultiGetRequest requests = RequestConverter.buildMultiGetRequest(location
+                    .getRegionInfo().getRegionName(), getsByRegionEntry.getValue(), true, false);
+                MultiGetResponse responses = server.multiGet(null, requests);
+                return responses.getExistsList();
+              } catch (ServiceException se) {
+                throw ProtobufUtil.getRemoteException(se);
+              }
+            }
+          }.withRetries();
+        }
+      };
+      futures.put(getsByRegionEntry.getKey(), pool.submit(callable));
+    }
+
+    // step 3: collect the failures and successes
+    Map<Integer, List<Boolean>> responses = new HashMap<Integer, List<Boolean>>();
+    for (final Map.Entry<Integer, List<Get>> sortedGetEntry : getsByRegion.entrySet()) {
+      try {
+        Future<List<Boolean>> future = futures.get(sortedGetEntry.getKey());
+        List<Boolean> resp = future.get();
+
+        if (resp == null) {
+          LOG.warn("Failed for gets on region: " + sortedGetEntry.getKey());
+        }
+        responses.put(sortedGetEntry.getKey(), resp);
+      } catch (ExecutionException e) {
+        LOG.warn("Failed for gets on region: " + sortedGetEntry.getKey());
+      } catch (InterruptedException e) {
+        LOG.warn("Failed for gets on region: " + sortedGetEntry.getKey());
+        Thread.currentThread().interrupt();
+      }
+    }
+    Boolean[] results = new Boolean[sortedGetsList.size()];
+
+    // step 4: build the response.
+    Map<Integer, Integer> indexes = new HashMap<Integer, Integer>();
+    for (int i = 0; i < sortedGetsList.size(); i++) {
+      Integer regionInfoIndex = getToRegionIndexMap.get(sortedGetsList.get(i).getGet());
+      Integer index = indexes.get(regionInfoIndex);
+      if (index == null) {
+        index = 0;
+      }
+      results[sortedGetsList.get(i).getInitialIndex()] = responses.get(regionInfoIndex).get(index);
+      indexes.put(regionInfoIndex, index + 1);
+    }
+
+    return results;
+  }
+
+  /**
    * {@inheritDoc}
    */
   @Override

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java Wed Jan 30 15:10:40 2013
@@ -64,7 +64,7 @@ public interface HTableInterface extends
   HTableDescriptor getTableDescriptor() throws IOException;
 
   /**
-   * Test for the existence of columns in the table, as specified in the Get.
+   * Test for the existence of columns in the table, as specified by the Get.
    * <p>
    *
    * This will return true if the Get matches one or more keys, false if not.
@@ -80,6 +80,23 @@ public interface HTableInterface extends
   boolean exists(Get get) throws IOException;
 
   /**
+   * Test for the existence of columns in the table, as specified by the Gets.
+   * <p>
+   *
+   * This will return an array of booleans. Each value will be true if the related Get matches
+   * one or more keys, false if not.
+   * <p>
+   *
+   * This is a server-side call so it prevents any data from being transfered to
+   * the client.
+   *
+   * @param gets the Gets
+   * @return Array of Boolean true if the specified Get matches one or more keys, false if not
+   * @throws IOException e
+   */
+  Boolean[] exists(List<Get> gets) throws IOException;
+
+  /**
    * Method that does a batch call on Deletes, Gets, Puts, Increments, Appends and RowMutations.
    * The ordering of execution of the actions is not defined. Meaning if you do a Put and a
    * Get in the same {@link #batch} call, you will not necessarily be

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java Wed Jan 30 15:10:40 2013
@@ -351,6 +351,11 @@ public class HTablePool implements Close
     }
 
     @Override
+    public Boolean[] exists(List<Get> gets) throws IOException {
+      return table.exists(gets);
+    }
+
+    @Override
     public void batch(List<? extends Row> actions, Object[] results) throws IOException,
         InterruptedException {
       table.batch(actions, results);

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java Wed Jan 30 15:10:40 2013
@@ -47,6 +47,8 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.*;
 import java.util.concurrent.ConcurrentMap;
 import java.util.jar.JarEntry;
@@ -232,7 +234,7 @@ public abstract class CoprocessorHost<E 
       // NOTE: Path.toURL is deprecated (toURI instead) but the URLClassLoader
       // unsurprisingly wants URLs, not URIs; so we will use the deprecated
       // method which returns URLs for as long as it is available
-      List<URL> paths = new ArrayList<URL>();
+      final List<URL> paths = new ArrayList<URL>();
       URL url = dst.getCanonicalFile().toURL();
       paths.add(url);
 
@@ -250,7 +252,13 @@ public abstract class CoprocessorHost<E 
       }
       jarFile.close();
 
-      cl = new CoprocessorClassLoader(paths, this.getClass().getClassLoader());
+      cl = AccessController.doPrivileged(new PrivilegedAction<CoprocessorClassLoader>() {
+              @Override
+              public CoprocessorClassLoader run() {
+                return new CoprocessorClassLoader(paths, this.getClass().getClassLoader());
+              }
+            });
+
       // cache cp classloader as a weak value, will be GC'ed when no reference left
       ClassLoader prev = classLoadersCache.putIfAbsent(path, cl);
       if (prev != null) {
@@ -472,6 +480,10 @@ public abstract class CoprocessorHost<E 
         return table.exists(get);
       }
 
+      public Boolean[] exists(List<Get> gets) throws IOException{
+        return table.exists(gets);
+      }
+
       public void put(Put put) throws IOException {
         table.put(put);
       }

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java Wed Jan 30 15:10:40 2013
@@ -68,6 +68,7 @@ import org.apache.hadoop.hbase.protobuf.
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.Condition;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiAction;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.Mutate;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.Mutate.ColumnValue;
@@ -166,7 +167,7 @@ public final class RequestConverter {
    * @param regionName the name of the region to get
    * @param get the client Get
    * @param existenceOnly indicate if check row existence only
-   * @return a protocol buffer GetReuqest
+   * @return a protocol buffer GetRequest
    */
   public static GetRequest buildGetRequest(final byte[] regionName,
       final Get get, final boolean existenceOnly) throws IOException {
@@ -180,6 +181,27 @@ public final class RequestConverter {
   }
 
   /**
+   * Create a protocol buffer MultiGetRequest for client Gets All gets are going to be run against
+   * the same region.
+   * @param regionName the name of the region to get from
+   * @param gets the client Gets
+   * @param existenceOnly indicate if check rows existence only
+   * @return a protocol buffer MultiGetRequest
+   */
+  public static MultiGetRequest buildMultiGetRequest(final byte[] regionName, final List<Get> gets,
+      final boolean existenceOnly, final boolean closestRowBefore) throws IOException {
+    MultiGetRequest.Builder builder = MultiGetRequest.newBuilder();
+    RegionSpecifier region = buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName);
+    builder.setExistenceOnly(existenceOnly);
+    builder.setClosestRowBefore(closestRowBefore);
+    builder.setRegion(region);
+    for (Get get : gets) {
+      builder.addGet(ProtobufUtil.toGet(get));
+    }
+    return builder.build();
+  }
+
+  /**
    * Create a protocol buffer MutateRequest for a client increment
    *
    * @param regionName

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java Wed Jan 30 15:10:40 2013
@@ -150,6 +150,8 @@ import org.apache.hadoop.hbase.protobuf.
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.CoprocessorServiceResponse;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetResponse;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetRequest;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetResponse;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiResponse;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.Mutate;
@@ -221,7 +223,7 @@ import com.google.protobuf.ServiceExcept
  */
 @InterfaceAudience.Private
 @SuppressWarnings("deprecation")
-public class  HRegionServer implements ClientProtocol,
+public class HRegionServer implements ClientProtocol,
     AdminProtocol, Runnable, RegionServerServices, HBaseRPCErrorHandler, LastSequenceId {
 
   public static final Log LOG = LogFactory.getLog(HRegionServer.class);
@@ -2729,6 +2731,64 @@ public class  HRegionServer implements C
   }
 
   /**
+   * Get multi data from a table.
+   *
+   * @param controller the RPC controller
+   * @param request multi-the get request
+   * @throws ServiceException
+   */
+  @Override
+  public MultiGetResponse multiGet(final RpcController controller, final MultiGetRequest request)
+      throws ServiceException {
+    long before = EnvironmentEdgeManager.currentTimeMillis();
+    try {
+      requestCount.add(request.getGetCount());
+      HRegion region = getRegion(request.getRegion());
+      MultiGetResponse.Builder builder = MultiGetResponse.newBuilder();
+      for (ClientProtos.Get get: request.getGetList())
+      {
+        Boolean existence = null;
+        Result r = null;
+        if (request.getClosestRowBefore()) {
+          if (get.getColumnCount() != 1) {
+            throw new DoNotRetryIOException(
+              "get ClosestRowBefore supports one and only one family now, not "
+                + get.getColumnCount() + " families");
+          }
+          byte[] row = get.getRow().toByteArray();
+          byte[] family = get.getColumn(0).getFamily().toByteArray();
+          r = region.getClosestRowBefore(row, family);
+        } else {
+          Get clientGet = ProtobufUtil.toGet(get);
+          if (request.getExistenceOnly() && region.getCoprocessorHost() != null) {
+            existence = region.getCoprocessorHost().preExists(clientGet);
+          }
+          if (existence == null) {
+            r = region.get(clientGet);
+            if (request.getExistenceOnly()) {
+              boolean exists = r != null && !r.isEmpty();
+              if (region.getCoprocessorHost() != null) {
+                exists = region.getCoprocessorHost().postExists(clientGet, exists);
+              }
+              existence = exists;
+            }
+          }
+        }
+        if (existence != null) {
+          builder.addExists(existence.booleanValue());
+        } else if (r != null) {
+          builder.addResult(ProtobufUtil.toResult(r));
+        }
+      }
+      return builder.build();
+    } catch (IOException ie) {
+      throw new ServiceException(ie);
+    } finally {
+      metricsRegionServer.updateGet(EnvironmentEdgeManager.currentTimeMillis() - before);
+    }
+  }
+
+  /**
    * Mutate data in a table.
    *
    * @param controller the RPC controller

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java Wed Jan 30 15:10:40 2013
@@ -344,6 +344,19 @@ public class RemoteHTable implements HTa
     return (result != null && !(result.isEmpty()));
   }
 
+  /**
+   * exists(List) is really a list of get() calls. Just use get().
+   * @param gets list of Get to test for the existence
+   */
+  public Boolean[] exists(List<Get> gets) throws IOException {
+    LOG.warn("exists(List<Get>) is really list of get() calls, just use get()");
+    Boolean[] results = new Boolean[gets.size()];
+    for (int i = 0; i < results.length; i++) {
+      results[i] = exists(gets.get(i));
+    }
+    return results;
+  }
+
   public void put(Put put) throws IOException {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();

Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide3.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide3.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide3.java (original)
+++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide3.java Wed Jan 30 15:10:40 2013
@@ -19,9 +19,11 @@
  */
 package org.apache.hadoop.hbase.client;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
@@ -52,10 +54,15 @@ public class TestFromClientSide3 {
   private static byte[] FAMILY = Bytes.toBytes("testFamily");
   private static Random random = new Random();
   private static int SLAVES = 3;
+  private static byte [] ROW = Bytes.toBytes("testRow");
+  private static final byte[] ANOTHERROW = Bytes.toBytes("anotherrow");
+  private static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
+  private static byte [] VALUE = Bytes.toBytes("testValue");
   private final static byte[] COL_QUAL = Bytes.toBytes("f1");
   private final static byte[] VAL_BYTES = Bytes.toBytes("v1");
   private final static byte[] ROW_BYTES = Bytes.toBytes("r1");
 
+
   /**
    * @throws java.lang.Exception
    */
@@ -261,6 +268,116 @@ public class TestFromClientSide3 {
   }
 
   @Test
+  public void testHTableExistsMethodSingleRegionSingleGet() throws Exception {
+
+    // Test with a single region table.
+
+    HTable table = TEST_UTIL.createTable(
+      Bytes.toBytes("testHTableExistsMethodSingleRegionSingleGet"), new byte[][] { FAMILY });
+
+    Put put = new Put(ROW);
+    put.add(FAMILY, QUALIFIER, VALUE);
+
+    Get get = new Get(ROW);
+
+    boolean exist = table.exists(get);
+    assertEquals(exist, false);
+
+    table.put(put);
+
+    exist = table.exists(get);
+    assertEquals(exist, true);
+  }
+
+  public void testHTableExistsMethodSingleRegionMultipleGets() throws Exception {
+
+    HTable table = TEST_UTIL.createTable(
+      Bytes.toBytes("testHTableExistsMethodSingleRegionMultipleGets"), new byte[][] { FAMILY });
+
+    Put put = new Put(ROW);
+    put.add(FAMILY, QUALIFIER, VALUE);
+    table.put(put);
+
+    List<Get> gets = new ArrayList<Get>();
+    gets.add(new Get(ROW));
+    gets.add(null);
+    gets.add(new Get(ANOTHERROW));
+
+    Boolean[] results = table.exists(gets);
+    assertEquals(results[0], true);
+    assertEquals(results[1], false);
+    assertEquals(results[2], false);
+  }
+
+  @Test
+  public void testHTableExistsMethodMultipleRegionsSingleGet() throws Exception {
+
+    HTable table = TEST_UTIL.createTable(
+      Bytes.toBytes("testHTableExistsMethodMultipleRegionsSingleGet"), new byte[][] { FAMILY }, 1,
+      new byte[] { 0x00 }, new byte[] { (byte) 0xff }, 255);
+    Put put = new Put(ROW);
+    put.add(FAMILY, QUALIFIER, VALUE);
+
+    Get get = new Get(ROW);
+
+    boolean exist = table.exists(get);
+    assertEquals(exist, false);
+
+    table.put(put);
+
+    exist = table.exists(get);
+    assertEquals(exist, true);
+  }
+
+  @Test
+  public void testHTableExistsMethodMultipleRegionsMultipleGets() throws Exception {
+    HTable table = TEST_UTIL.createTable(
+      Bytes.toBytes("testHTableExistsMethodMultipleRegionsMultipleGets"), new byte[][] { FAMILY },
+      1, new byte[] { 0x00 }, new byte[] { (byte) 0xff }, 255);
+    Put put = new Put(ROW);
+    put.add(FAMILY, QUALIFIER, VALUE);
+    table.put (put);
+
+    List<Get> gets = new ArrayList<Get>();
+    gets.add(new Get(ANOTHERROW));
+    gets.add(new Get(Bytes.add(ROW, new byte[] { 0x00 })));
+    gets.add(new Get(ROW));
+    gets.add(new Get(Bytes.add(ANOTHERROW, new byte[] { 0x00 })));
+
+    Boolean[] results = table.exists(gets);
+    assertEquals(results[0], false);
+    assertEquals(results[1], false);
+    assertEquals(results[2], true);
+    assertEquals(results[3], false);
+
+    // Test with the first region.
+    put = new Put(new byte[] { 0x00 });
+    put.add(FAMILY, QUALIFIER, VALUE);
+    table.put(put);
+
+    gets = new ArrayList<Get>();
+    gets.add(new Get(new byte[] { 0x00 }));
+    gets.add(new Get(new byte[] { 0x00, 0x00 }));
+    results = table.exists(gets);
+    assertEquals(results[0], true);
+    assertEquals(results[1], false);
+
+    // Test with the last region
+    put = new Put(new byte[] { (byte) 0xff, (byte) 0xff });
+    put.add(FAMILY, QUALIFIER, VALUE);
+    table.put(put);
+
+    gets = new ArrayList<Get>();
+    gets.add(new Get(new byte[] { (byte) 0xff }));
+    gets.add(new Get(new byte[] { (byte) 0xff, (byte) 0xff }));
+    gets.add(new Get(new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff }));
+    results = table.exists(gets);
+    assertEquals(results[0], false);
+    assertEquals(results[1], true);
+    assertEquals(results[2], false);
+  }
+
+  @Test
   public void testGetEmptyRow() throws Exception {
     //Create a table and put in 1 row
     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();

Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java?rev=1440454&r1=1440453&r2=1440454&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java (original)
+++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java Wed Jan 30 15:10:40 2013
@@ -67,6 +67,8 @@ import org.apache.hadoop.hbase.protobuf.
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.BulkLoadHFileResponse;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetResponse;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetRequest;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiGetResponse;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateRequest;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateResponse;
@@ -325,6 +327,22 @@ class MockRegionServer implements AdminP
   }
 
   @Override
+  public MultiGetResponse multiGet(RpcController controller, MultiGetRequest requests)
+      throws ServiceException {
+    byte[] regionName = requests.getRegion().getValue().toByteArray();
+    Map<byte [], Result> m = this.gets.get(regionName);
+    MultiGetResponse.Builder builder = MultiGetResponse.newBuilder();
+    if (m != null) {
+      for (ClientProtos.Get get: requests.getGetList()) {
+        byte[] row = get.getRow().toByteArray();
+        builder.addResult(ProtobufUtil.toResult(m.get(row)));
+      }
+    }
+    return builder.build();
+  }
+
+
+  @Override
   public MutateResponse mutate(RpcController controller, MutateRequest request)
       throws ServiceException {
     // TODO Auto-generated method stub