You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by zh...@apache.org on 2017/12/20 01:30:25 UTC

[18/24] hbase git commit: HBASE-19494 Create simple WALKey filter that can be plugged in on the Replication Sink

HBASE-19494 Create simple WALKey filter that can be plugged in on the Replication Sink

Implement new WALEntrySinkFilter (as opposed to WALEntryFilter) and
specify the implmentation (with a no-param constructor) in config
using property hbase.replication.sink.walentrysinkfilter

Signed-off-by: wolfgang hoschek whoscheck@cloudera.com


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/32f6fd41
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/32f6fd41
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/32f6fd41

Branch: refs/heads/HBASE-19397
Commit: 32f6fd41c274a955338b7c43ab80309b9adbba0d
Parents: df351e4
Author: Michael Stack <st...@apache.org>
Authored: Mon Dec 18 12:57:53 2017 -0800
Committer: Michael Stack <st...@apache.org>
Committed: Tue Dec 19 13:48:59 2017 -0800

----------------------------------------------------------------------
 .../hbase/replication/WALEntryFilter.java       |   9 +-
 .../regionserver/ReplicationSink.java           |  47 +-
 .../regionserver/WALEntrySinkFilter.java        |  57 ++
 .../regionserver/TestWALEntrySinkFilter.java    | 549 +++++++++++++++++++
 4 files changed, 650 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/32f6fd41/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/WALEntryFilter.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/WALEntryFilter.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/WALEntryFilter.java
index 0024b12..417f868 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/WALEntryFilter.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/WALEntryFilter.java
@@ -25,10 +25,16 @@ import org.apache.hadoop.hbase.wal.WAL.Entry;
 /**
  * A Filter for WAL entries before being sent over to replication. Multiple
  * filters might be chained together using {@link ChainWALEntryFilter}.
+ * Applied on the replication source side.
+ * <p>There is also a filter that can be installed on the sink end of a replication stream.
+ * See {@link org.apache.hadoop.hbase.replication.regionserver.WALEntrySinkFilter}. Certain
+ * use-cases may need such a facility but better to filter here on the source side rather
+ * than later, after the edit arrives at the sink.</p>
+ * @see org.apache.hadoop.hbase.replication.regionserver.WALEntrySinkFilter for filtering
+ * replication on the sink-side.
  */
 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.REPLICATION)
 public interface WALEntryFilter {
-
   /**
    * Applies the filter, possibly returning a different Entry instance.
    * If null is returned, the entry will be skipped.
@@ -37,5 +43,4 @@ public interface WALEntryFilter {
    * no cells will cause the entry to be skipped for replication.
    */
   public Entry filter(Entry entry);
-
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/32f6fd41/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/ReplicationSink.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/ReplicationSink.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/ReplicationSink.java
index 2f9f9c5..2194796 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/ReplicationSink.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/ReplicationSink.java
@@ -89,6 +89,7 @@ public class ReplicationSink {
   // Number of hfiles that we successfully replicated
   private long hfilesReplicated = 0;
   private SourceFSConfigurationProvider provider;
+  private WALEntrySinkFilter walEntrySinkFilter;
 
   /**
    * Create a sink for replication
@@ -102,7 +103,7 @@ public class ReplicationSink {
     this.conf = HBaseConfiguration.create(conf);
     decorateConf();
     this.metrics = new MetricsSink();
-
+    this.walEntrySinkFilter = setupWALEntrySinkFilter();
     String className =
         conf.get("hbase.replication.source.fs.conf.provider",
           DefaultSourceFSConfigurationProvider.class.getCanonicalName());
@@ -116,6 +117,22 @@ public class ReplicationSink {
     }
   }
 
+  private WALEntrySinkFilter setupWALEntrySinkFilter() throws IOException {
+    Class<?> walEntryFilterClass =
+        this.conf.getClass(WALEntrySinkFilter.WAL_ENTRY_FILTER_KEY, null);
+    WALEntrySinkFilter filter = null;
+    try {
+      filter = walEntryFilterClass == null? null:
+          (WALEntrySinkFilter)walEntryFilterClass.newInstance();
+    } catch (Exception e) {
+      LOG.warn("Failed to instantiate " + walEntryFilterClass);
+    }
+    if (filter != null) {
+      filter.init(getConnection());
+    }
+    return filter;
+  }
+
   /**
    * decorate the Configuration object to make replication more receptive to delays:
    * lessen the timeout and numTries.
@@ -134,8 +151,6 @@ public class ReplicationSink {
   /**
    * Replicate this array of entries directly into the local cluster using the native client. Only
    * operates against raw protobuf type saving on a conversion from pb to pojo.
-   * @param entries
-   * @param cells
    * @param replicationClusterId Id which will uniquely identify source cluster FS client
    *          configurations in the replication configuration directory
    * @param sourceBaseNamespaceDirPath Path that point to the source cluster base namespace
@@ -147,7 +162,6 @@ public class ReplicationSink {
       String replicationClusterId, String sourceBaseNamespaceDirPath,
       String sourceHFileArchiveDirPath) throws IOException {
     if (entries.isEmpty()) return;
-    if (cells == null) throw new NullPointerException("TODO: Add handling of null CellScanner");
     // Very simple optimization where we batch sequences of rows going
     // to the same table.
     try {
@@ -162,8 +176,21 @@ public class ReplicationSink {
       for (WALEntry entry : entries) {
         TableName table =
             TableName.valueOf(entry.getKey().getTableName().toByteArray());
+        if (this.walEntrySinkFilter != null) {
+          if (this.walEntrySinkFilter.filter(table, entry.getKey().getWriteTime())) {
+            // Skip Cells in CellScanner associated with this entry.
+            int count = entry.getAssociatedCellCount();
+            for (int i = 0; i < count; i++) {
+              // Throw index out of bounds if our cell count is off
+              if (!cells.advance()) {
+                throw new ArrayIndexOutOfBoundsException("Expected=" + count + ", index=" + i);
+              }
+            }
+            continue;
+          }
+        }
         Cell previousCell = null;
-        Mutation m = null;
+        Mutation mutation = null;
         int count = entry.getAssociatedCellCount();
         for (int i = 0; i < count; i++) {
           // Throw index out of bounds if our cell count is off
@@ -181,7 +208,7 @@ public class ReplicationSink {
             // Handle wal replication
             if (isNewRowOrType(previousCell, cell)) {
               // Create new mutation
-              m =
+              mutation =
                   CellUtil.isDelete(cell) ? new Delete(cell.getRowArray(), cell.getRowOffset(),
                       cell.getRowLength()) : new Put(cell.getRowArray(), cell.getRowOffset(),
                       cell.getRowLength());
@@ -189,13 +216,13 @@ public class ReplicationSink {
               for (HBaseProtos.UUID clusterId : entry.getKey().getClusterIdsList()) {
                 clusterIds.add(toUUID(clusterId));
               }
-              m.setClusterIds(clusterIds);
-              addToHashMultiMap(rowMap, table, clusterIds, m);
+              mutation.setClusterIds(clusterIds);
+              addToHashMultiMap(rowMap, table, clusterIds, mutation);
             }
             if (CellUtil.isDelete(cell)) {
-              ((Delete) m).add(cell);
+              ((Delete) mutation).add(cell);
             } else {
-              ((Put) m).add(cell);
+              ((Put) mutation).add(cell);
             }
             previousCell = cell;
           }

http://git-wip-us.apache.org/repos/asf/hbase/blob/32f6fd41/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/WALEntrySinkFilter.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/WALEntrySinkFilter.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/WALEntrySinkFilter.java
new file mode 100644
index 0000000..f0b13e1
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/WALEntrySinkFilter.java
@@ -0,0 +1,57 @@
+/*
+ * 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.hadoop.hbase.replication.regionserver;
+
+import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Implementations are installed on a Replication Sink called from inside
+ * ReplicationSink#replicateEntries to filter replicated WALEntries based off WALEntry attributes.
+ * Currently only table name and replication write time are exposed (WALEntry is a private,
+ * internal class so we cannot pass it here). To install, set
+ * <code>hbase.replication.sink.walentryfilter</code> to the name of the implementing
+ * class. Implementing class must have a no-param Constructor.
+ * <p>This filter is of limited use. It is better to filter on the replication source rather than
+ * here after the edits have been shipped on the replication sink. That said, applications such
+ * as the hbase-indexer want to filter out any edits that were made before replication was enabled.
+ * @see org.apache.hadoop.hbase.replication.WALEntryFilter for filtering on the replication
+ * source-side.
+ */
+@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.REPLICATION)
+public interface WALEntrySinkFilter {
+  /**
+   * Name of configuration to set with name of implementing WALEntrySinkFilter class.
+   */
+  public static final String WAL_ENTRY_FILTER_KEY = "hbase.replication.sink.walentrysinkfilter";
+
+  /**
+   * Called after Construction.
+   * Use passed Connection to keep any context the filter might need.
+   */
+  void init(Connection connection);
+
+  /**
+   * @param table Table edit is destined for.
+   * @param writeTime Time at which the edit was created on the source.
+   * @return True if we are to filter out the edit.
+   */
+  boolean filter(TableName table, long writeTime);
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/32f6fd41/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestWALEntrySinkFilter.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestWALEntrySinkFilter.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestWALEntrySinkFilter.java
new file mode 100644
index 0000000..0761178
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestWALEntrySinkFilter.java
@@ -0,0 +1,549 @@
+/*
+ * 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.hadoop.hbase.replication.regionserver;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.CategoryBasedTimeout;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellBuilder;
+import org.apache.hadoop.hbase.CellBuilderFactory;
+import org.apache.hadoop.hbase.CellBuilderType;
+import org.apache.hadoop.hbase.CellScanner;
+import org.apache.hadoop.hbase.CompareOperator;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.Stoppable;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Admin;
+import org.apache.hadoop.hbase.client.Append;
+import org.apache.hadoop.hbase.client.BufferedMutator;
+import org.apache.hadoop.hbase.client.BufferedMutatorParams;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.Durability;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.Increment;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.RegionLocator;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Row;
+import org.apache.hadoop.hbase.client.RowMutations;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.client.TableBuilder;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.client.coprocessor.Batch;
+import org.apache.hadoop.hbase.filter.CompareFilter;
+import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
+import org.apache.hadoop.hbase.security.User;
+import org.apache.hadoop.hbase.shaded.com.google.protobuf.ByteString;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
+import org.apache.hadoop.hbase.testclassification.ReplicationTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+import org.junit.rules.TestRule;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Simple test of sink-side wal entry filter facility.
+ */
+@Category({ReplicationTests.class, SmallTests.class})
+public class TestWALEntrySinkFilter {
+  private static final Log LOG = LogFactory.getLog(TestReplicationSink.class);
+  @Rule public TestName name = new TestName();
+  @Rule public final TestRule timeout = CategoryBasedTimeout.builder().
+      withTimeout(this.getClass()).
+      withLookingForStuckThread(true).
+      build();
+  static final int BOUNDARY = 5;
+  static final AtomicInteger UNFILTERED = new AtomicInteger();
+  static final AtomicInteger FILTERED = new AtomicInteger();
+
+  /**
+   * Implemetentation of Stoppable to pass into ReplicationSink.
+   */
+  private static Stoppable STOPPABLE = new Stoppable() {
+    private final AtomicBoolean stop = new AtomicBoolean(false);
+
+    @Override
+    public boolean isStopped() {
+      return this.stop.get();
+    }
+
+    @Override
+    public void stop(String why) {
+      LOG.info("STOPPING BECAUSE: " + why);
+      this.stop.set(true);
+    }
+  };
+
+  /**
+   * Test filter.
+   * Filter will filter out any write time that is <= 5 (BOUNDARY). We count how many items we
+   * filter out and we count how many cells make it through for distribution way down below in the
+   * Table#batch implementation. Puts in place a custom DevNullConnection so we can insert our
+   * counting Table.
+   * @throws IOException
+   */
+  @Test
+  public void testWALEntryFilter() throws IOException {
+    Configuration conf = HBaseConfiguration.create();
+    // Make it so our filter is instantiated on construction of ReplicationSink.
+    conf.setClass(WALEntrySinkFilter.WAL_ENTRY_FILTER_KEY,
+        IfTimeIsGreaterThanBOUNDARYWALEntrySinkFilterImpl.class, WALEntrySinkFilter.class);
+    conf.setClass("hbase.client.connection.impl", DevNullConnection.class,
+        Connection.class);
+    ReplicationSink sink = new ReplicationSink(conf, STOPPABLE);
+    // Create some dumb walentries.
+    List< org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.WALEntry > entries =
+        new ArrayList<>();
+    AdminProtos.WALEntry.Builder entryBuilder = AdminProtos.WALEntry.newBuilder();
+    // Need a tablename.
+    ByteString tableName =
+        ByteString.copyFromUtf8(TableName.valueOf(this.name.getMethodName()).toString());
+    // Add WALEdit Cells to Cells List. The way edits arrive at the sink is with protos
+    // describing the edit with all Cells from all edits aggregated in a single CellScanner.
+    final List<Cell> cells = new ArrayList<>();
+    int count = BOUNDARY * 2;
+    for(int i = 0; i < count; i++) {
+      byte [] bytes = Bytes.toBytes(i);
+      // Create a wal entry. Everything is set to the current index as bytes or int/long.
+      entryBuilder.clear();
+      entryBuilder.setKey(entryBuilder.getKeyBuilder().
+          setLogSequenceNumber(i).
+          setEncodedRegionName(ByteString.copyFrom(bytes)).
+          setWriteTime(i).
+          setTableName(tableName).build());
+      // Lets have one Cell associated with each WALEdit.
+      entryBuilder.setAssociatedCellCount(1);
+      entries.add(entryBuilder.build());
+      // We need to add a Cell per WALEdit to the cells array.
+      CellBuilder cellBuilder = CellBuilderFactory.create(CellBuilderType.DEEP_COPY);
+      // Make cells whose row, family, cell, value, and ts are == 'i'.
+      Cell cell = cellBuilder.
+          setRow(bytes).
+          setFamily(bytes).
+          setQualifier(bytes).
+          setType(Cell.DataType.Put).
+          setTimestamp(i).
+          setValue(bytes).build();
+      cells.add(cell);
+    }
+    // Now wrap our cells array in a CellScanner that we can pass in to replicateEntries. It has
+    // all Cells from all the WALEntries made above.
+    CellScanner cellScanner = new CellScanner() {
+      // Set to -1 because advance gets called before current.
+      int index = -1;
+
+      @Override
+      public Cell current() {
+        return cells.get(index);
+      }
+
+      @Override
+      public boolean advance() throws IOException {
+        index++;
+        return index < cells.size();
+      }
+    };
+    // Call our sink.
+    sink.replicateEntries(entries, cellScanner, null, null, null);
+    // Check what made it through and what was filtered.
+    assertTrue(FILTERED.get() > 0);
+    assertTrue(UNFILTERED.get() > 0);
+    assertEquals(count, FILTERED.get() + UNFILTERED.get());
+  }
+
+  /**
+   * Simple filter that will filter out any entry wholse writeTime is <= 5.
+   */
+  public static class IfTimeIsGreaterThanBOUNDARYWALEntrySinkFilterImpl implements WALEntrySinkFilter {
+    public IfTimeIsGreaterThanBOUNDARYWALEntrySinkFilterImpl() {}
+
+    @Override
+    public void init(Connection connection) {
+      // Do nothing.
+    }
+
+    @Override
+    public boolean filter(TableName table, long writeTime) {
+      boolean b = writeTime <= BOUNDARY;
+      if (b) {
+        FILTERED.incrementAndGet();
+      }
+      return b;
+    }
+  }
+
+  /**
+   * A DevNull Connection whose only purpose is checking what edits made it through. See down in
+   * {@link Table#batch(List, Object[])}.
+   */
+  public static class DevNullConnection implements Connection {
+    private final Configuration configuration;
+
+    DevNullConnection(Configuration configuration, ExecutorService es, User user) {
+      this.configuration = configuration;
+    }
+
+    @Override
+    public void abort(String why, Throwable e) {
+
+    }
+
+    @Override
+    public boolean isAborted() {
+      return false;
+    }
+
+    @Override
+    public Configuration getConfiguration() {
+      return this.configuration;
+    }
+
+    @Override
+    public BufferedMutator getBufferedMutator(TableName tableName) throws IOException {
+      return null;
+    }
+
+    @Override
+    public BufferedMutator getBufferedMutator(BufferedMutatorParams params) throws IOException {
+      return null;
+    }
+
+    @Override
+    public RegionLocator getRegionLocator(TableName tableName) throws IOException {
+      return null;
+    }
+
+    @Override
+    public Admin getAdmin() throws IOException {
+      return null;
+    }
+
+    @Override
+    public void close() throws IOException {
+
+    }
+
+    @Override
+    public boolean isClosed() {
+      return false;
+    }
+
+    @Override
+    public TableBuilder getTableBuilder(final TableName tableName, ExecutorService pool) {
+      return new TableBuilder() {
+        @Override
+        public TableBuilder setOperationTimeout(int timeout) {
+          return this;
+        }
+
+        @Override
+        public TableBuilder setRpcTimeout(int timeout) {
+          return this;
+        }
+
+        @Override
+        public TableBuilder setReadRpcTimeout(int timeout) {
+          return this;
+        }
+
+        @Override
+        public TableBuilder setWriteRpcTimeout(int timeout) {
+          return this;
+        }
+
+        @Override
+        public Table build() {
+          return new Table() {
+            @Override
+            public TableName getName() {
+              return tableName;
+            }
+
+            @Override
+            public Configuration getConfiguration() {
+              return configuration;
+            }
+
+            @Override
+            public HTableDescriptor getTableDescriptor() throws IOException {
+              return null;
+            }
+
+            @Override
+            public TableDescriptor getDescriptor() throws IOException {
+              return null;
+            }
+
+            @Override
+            public boolean exists(Get get) throws IOException {
+              return false;
+            }
+
+            @Override
+            public boolean[] exists(List<Get> gets) throws IOException {
+              return new boolean[0];
+            }
+
+            @Override
+            public void batch(List<? extends Row> actions, Object[] results) throws IOException, InterruptedException {
+              for (Row action: actions) {
+                // Row is the index of the loop above where we make WALEntry and Cells.
+                int row = Bytes.toInt(action.getRow());
+                assertTrue("" + row, row> BOUNDARY);
+                UNFILTERED.incrementAndGet();
+              }
+            }
+
+            @Override
+            public <R> void batchCallback(List<? extends Row> actions, Object[] results, Batch.Callback<R> callback) throws IOException, InterruptedException {
+
+            }
+
+            @Override
+            public Result get(Get get) throws IOException {
+              return null;
+            }
+
+            @Override
+            public Result[] get(List<Get> gets) throws IOException {
+              return new Result[0];
+            }
+
+            @Override
+            public ResultScanner getScanner(Scan scan) throws IOException {
+              return null;
+            }
+
+            @Override
+            public ResultScanner getScanner(byte[] family) throws IOException {
+              return null;
+            }
+
+            @Override
+            public ResultScanner getScanner(byte[] family, byte[] qualifier) throws IOException {
+              return null;
+            }
+
+            @Override
+            public void put(Put put) throws IOException {
+
+            }
+
+            @Override
+            public void put(List<Put> puts) throws IOException {
+
+            }
+
+            @Override
+            public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, byte[] value, Put put) throws IOException {
+              return false;
+            }
+
+            @Override
+            public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value, Put put) throws IOException {
+              return false;
+            }
+
+            @Override
+            public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, CompareOperator op, byte[] value, Put put) throws IOException {
+              return false;
+            }
+
+            @Override
+            public void delete(Delete delete) throws IOException {
+
+            }
+
+            @Override
+            public void delete(List<Delete> deletes) throws IOException {
+
+            }
+
+            @Override
+            public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, byte[] value, Delete delete) throws IOException {
+              return false;
+            }
+
+            @Override
+            public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value, Delete delete) throws IOException {
+              return false;
+            }
+
+            @Override
+            public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, CompareOperator op, byte[] value, Delete delete) throws IOException {
+              return false;
+            }
+
+            @Override
+            public CheckAndMutateBuilder checkAndMutate(byte[] row, byte[] family) {
+              return null;
+            }
+
+            @Override
+            public void mutateRow(RowMutations rm) throws IOException {
+
+            }
+
+            @Override
+            public Result append(Append append) throws IOException {
+              return null;
+            }
+
+            @Override
+            public Result increment(Increment increment) throws IOException {
+              return null;
+            }
+
+            @Override
+            public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount) throws IOException {
+              return 0;
+            }
+
+            @Override
+            public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount, Durability durability) throws IOException {
+              return 0;
+            }
+
+            @Override
+            public void close() throws IOException {
+
+            }
+
+            @Override
+            public CoprocessorRpcChannel coprocessorService(byte[] row) {
+              return null;
+            }
+
+            @Override
+            public <T extends com.google.protobuf.Service, R> Map<byte[], R> coprocessorService(Class<T> service, byte[] startKey, byte[] endKey, Batch.Call<T, R> callable) throws com.google.protobuf.ServiceException, Throwable {
+              return null;
+            }
+
+            @Override
+            public <T extends com.google.protobuf.Service, R> void coprocessorService(Class<T> service, byte[] startKey, byte[] endKey, Batch.Call<T, R> callable, Batch.Callback<R> callback) throws com.google.protobuf.ServiceException, Throwable {
+
+            }
+
+            @Override
+            public <R extends com.google.protobuf.Message> Map<byte[], R> batchCoprocessorService(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, com.google.protobuf.Message request, byte[] startKey, byte[] endKey, R responsePrototype) throws com.google.protobuf.ServiceException, Throwable {
+              return null;
+            }
+
+            @Override
+            public <R extends com.google.protobuf.Message> void batchCoprocessorService(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, com.google.protobuf.Message request, byte[] startKey, byte[] endKey, R responsePrototype, Batch.Callback<R> callback) throws com.google.protobuf.ServiceException, Throwable {
+
+            }
+
+            @Override
+            public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value, RowMutations mutation) throws IOException {
+              return false;
+            }
+
+            @Override
+            public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier, CompareOperator op, byte[] value, RowMutations mutation) throws IOException {
+              return false;
+            }
+
+            @Override
+            public long getRpcTimeout(TimeUnit unit) {
+              return 0;
+            }
+
+            @Override
+            public int getRpcTimeout() {
+              return 0;
+            }
+
+            @Override
+            public void setRpcTimeout(int rpcTimeout) {
+
+            }
+
+            @Override
+            public long getReadRpcTimeout(TimeUnit unit) {
+              return 0;
+            }
+
+            @Override
+            public int getReadRpcTimeout() {
+              return 0;
+            }
+
+            @Override
+            public void setReadRpcTimeout(int readRpcTimeout) {
+
+            }
+
+            @Override
+            public long getWriteRpcTimeout(TimeUnit unit) {
+              return 0;
+            }
+
+            @Override
+            public int getWriteRpcTimeout() {
+              return 0;
+            }
+
+            @Override
+            public void setWriteRpcTimeout(int writeRpcTimeout) {
+
+            }
+
+            @Override
+            public long getOperationTimeout(TimeUnit unit) {
+              return 0;
+            }
+
+            @Override
+            public int getOperationTimeout() {
+              return 0;
+            }
+
+            @Override
+            public void setOperationTimeout(int operationTimeout) {
+
+            }
+          };
+        }
+      };
+    }
+  }
+}
+
+