You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2011/03/24 07:27:24 UTC

svn commit: r1084854 - in /hbase/trunk: ./ src/main/java/org/apache/hadoop/hbase/coprocessor/ src/main/java/org/apache/hadoop/hbase/regionserver/ src/test/java/org/apache/hadoop/hbase/coprocessor/

Author: apurtell
Date: Thu Mar 24 06:27:23 2011
New Revision: 1084854

URL: http://svn.apache.org/viewvc?rev=1084854&view=rev
Log:
HBASE-3583  Coprocessors: scannerNext and scannerClose hooks are called when HRegionInterface#get is invoked

Modified:
    hbase/trunk/CHANGES.txt
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java

Modified: hbase/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/trunk/CHANGES.txt?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/CHANGES.txt (original)
+++ hbase/trunk/CHANGES.txt Thu Mar 24 06:27:23 2011
@@ -46,6 +46,9 @@ Release 0.91.0 - Unreleased
    HBASE-3532  HRegion#equals is broken (Ted Yu via Stack)
    HBASE-3697  Admin actions that use MetaReader to iterate regions need to
                skip offline ones
+   HBASE-3583  Coprocessors: scannerNext and scannerClose hooks are called
+               when HRegionInterface#get is invoked (Mingjie Lai via
+               Andrew Purtell)
 
   IMPROVEMENTS
    HBASE-3290  Max Compaction Size (Nicolas Spiegelberg via Stack)  

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java Thu Mar 24 06:27:23 2011
@@ -206,14 +206,14 @@ public abstract class BaseRegionObserver
 
   @Override
   public boolean preScannerNext(final RegionCoprocessorEnvironment e,
-      final InternalScanner s, final List<KeyValue> results,
+      final InternalScanner s, final List<Result> results,
       final int limit, final boolean hasMore) throws IOException {
     return hasMore;
   }
 
   @Override
   public boolean postScannerNext(final RegionCoprocessorEnvironment e,
-      final InternalScanner s, final List<KeyValue> results, final int limit,
+      final InternalScanner s, final List<Result> results, final int limit,
       final boolean hasMore) throws IOException {
     return hasMore;
   }

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java Thu Mar 24 06:27:23 2011
@@ -492,7 +492,7 @@ public interface RegionObserver extends 
    * @throws IOException if an error occurred on the coprocessor
    */
   public boolean preScannerNext(final RegionCoprocessorEnvironment e,
-      final InternalScanner s, final List<KeyValue> result,
+      final InternalScanner s, final List<Result> result,
       final int limit, final boolean hasNext)
     throws IOException;
 
@@ -510,7 +510,7 @@ public interface RegionObserver extends 
    * @throws IOException if an error occurred on the coprocessor
    */
   public boolean postScannerNext(final RegionCoprocessorEnvironment e,
-      final InternalScanner s, final List<KeyValue> result, final int limit,
+      final InternalScanner s, final List<Result> result, final int limit,
       final boolean hasNext)
     throws IOException;
 

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java Thu Mar 24 06:27:23 2011
@@ -2461,14 +2461,6 @@ public class HRegion implements HeapSize
 
     public synchronized boolean next(List<KeyValue> outResults, int limit)
         throws IOException {
-      if (coprocessorHost != null) {
-        Boolean result = coprocessorHost.preScannerNext((InternalScanner)this,
-          outResults, limit);
-        if (result != null) {
-          return result.booleanValue();
-        }
-      }
-
       if (this.filterClosed) {
         throw new UnknownScannerException("Scanner was closed (timed out?) " +
             "after we renewed it. Could be caused by a very slow scanner " +
@@ -2484,11 +2476,6 @@ public class HRegion implements HeapSize
 
         boolean returnResult = nextInternal(limit);
 
-        if (coprocessorHost != null) {
-          returnResult = coprocessorHost.postScannerNext((InternalScanner)this,
-            results, limit, returnResult);
-        }
-
         outResults.addAll(results);
         resetFilters();
         if (isFilterDone()) {
@@ -2596,20 +2583,12 @@ public class HRegion implements HeapSize
               currentRow, 0, currentRow.length) <= isScan);
     }
 
-    public synchronized void close() throws IOException {
-      if (coprocessorHost != null) {
-        if (coprocessorHost.preScannerClose((InternalScanner)this)) {
-          return;
-        }
-      }
+    public synchronized void close() {
       if (storeHeap != null) {
         storeHeap.close();
         storeHeap = null;
       }
       this.filterClosed = true;
-      if (coprocessorHost != null) {
-        coprocessorHost.postScannerClose((InternalScanner)this);
-      }
     }
   }
 

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java Thu Mar 24 06:27:23 2011
@@ -1929,6 +1929,32 @@ public class HRegionServer implements HR
       List<Result> results = new ArrayList<Result>(nbRows);
       long currentScanResultSize = 0;
       List<KeyValue> values = new ArrayList<KeyValue>();
+
+      // Call coprocessor. Get region info from scanner.
+      HRegion region = null;
+      if (s instanceof HRegion.RegionScanner) {
+        HRegion.RegionScanner rs = (HRegion.RegionScanner) s;
+        region = getRegion(rs.getRegionName().getRegionName());
+      } else {
+        throw new IOException("InternalScanner implementation is expected " +
+            "to be HRegion.RegionScanner.");
+      }
+      if (region != null && region.getCoprocessorHost() != null) {
+        Boolean bypass = region.getCoprocessorHost().preScannerNext(s,
+            results, nbRows);
+        if (!results.isEmpty()) {
+          for (Result r : results) {
+            for (KeyValue kv : r.raw()) {
+              currentScanResultSize += kv.heapSize();
+            }
+          }
+        }
+        if (bypass != null) {
+          return ((HRegion.RegionScanner) s).isFilterDone() && results.isEmpty() ? null
+              : results.toArray(new Result[0]);
+        }
+      }
+
       for (int i = 0; i < nbRows
           && currentScanResultSize < maxScannerResultSize; i++) {
         requestCount.incrementAndGet();
@@ -1945,6 +1971,12 @@ public class HRegionServer implements HR
         }
         values.clear();
       }
+
+      // coprocessor postNext hook
+      if (region != null && region.getCoprocessorHost() != null) {
+        region.getCoprocessorHost().postScannerNext(s, results, nbRows, true);
+      }
+
       // Below is an ugly hack where we cast the InternalScanner to be a
       // HRegion.RegionScanner. The alternative is to change InternalScanner
       // interface but its used everywhere whereas we just need a bit of info
@@ -1967,10 +1999,33 @@ public class HRegionServer implements HR
       checkOpen();
       requestCount.incrementAndGet();
       String scannerName = String.valueOf(scannerId);
-      InternalScanner s = scanners.remove(scannerName);
+      InternalScanner s = scanners.get(scannerName);
+
+      HRegion region = null;
+      if (s != null) {
+        // call coprocessor.
+        if (s instanceof HRegion.RegionScanner) {
+          HRegion.RegionScanner rs = (HRegion.RegionScanner) s;
+          region = getRegion(rs.getRegionName().getRegionName());
+        } else {
+          throw new IOException("InternalScanner implementation is expected " +
+              "to be HRegion.RegionScanner.");
+        }
+        if (region != null && region.getCoprocessorHost() != null) {
+          if (region.getCoprocessorHost().preScannerClose(s)) {
+            return; // bypass
+          }
+        }
+      }
+
+      s = scanners.remove(scannerName);
       if (s != null) {
         s.close();
         this.leases.cancelLease(scannerName);
+
+        if (region != null && region.getCoprocessorHost() != null) {
+          region.getCoprocessorHost().postScannerClose(s);
+        }
       }
     } catch (Throwable t) {
       throw convertThrowableToIOE(cleanup(t));

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java Thu Mar 24 06:27:23 2011
@@ -911,7 +911,7 @@ public class RegionCoprocessorHost
    * @exception IOException Exception
    */
   public Boolean preScannerNext(final InternalScanner s,
-      final List<KeyValue> results, int limit) throws IOException {
+      final List<Result> results, int limit) throws IOException {
     try {
       boolean bypass = false;
       boolean hasNext = false;
@@ -941,7 +941,7 @@ public class RegionCoprocessorHost
    * @exception IOException Exception
    */
   public boolean postScannerNext(final InternalScanner s,
-      final List<KeyValue> results, final int limit, boolean hasMore)
+      final List<Result> results, final int limit, boolean hasMore)
       throws IOException {
     try {
       coprocessorLock.readLock().lock();

Modified: hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java (original)
+++ hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java Thu Mar 24 06:27:23 2011
@@ -35,6 +35,7 @@ import org.apache.hadoop.hbase.client.Ge
 import org.apache.hadoop.hbase.client.Increment;
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.hadoop.hbase.regionserver.InternalScanner;
 import org.apache.hadoop.hbase.util.Bytes;
 
 /**
@@ -68,6 +69,10 @@ public class SimpleRegionObserver extend
   boolean hadPostIncrement = false;
   boolean hadPreWALRestored = false;
   boolean hadPostWALRestored = false;
+  boolean hadPreScannerNext = false;
+  boolean hadPostScannerNext = false;
+  boolean hadPreScannerClose = false;
+  boolean hadPostScannerClose = false;
 
   @Override
   public void preOpen(RegionCoprocessorEnvironment e) {
@@ -135,6 +140,35 @@ public class SimpleRegionObserver extend
     hadPostCompact = true;
   }
 
+
+  @Override
+  public boolean preScannerNext(final RegionCoprocessorEnvironment e,
+      final InternalScanner s, final List<Result> results,
+      final int limit, final boolean hasMore) throws IOException {
+    hadPreScannerNext = true;
+    return hasMore;
+  }
+
+  @Override
+  public boolean postScannerNext(final RegionCoprocessorEnvironment e,
+      final InternalScanner s, final List<Result> results, final int limit,
+      final boolean hasMore) throws IOException {
+    hadPostScannerNext = true;
+    return hasMore;
+  }
+
+  @Override
+  public void preScannerClose(final RegionCoprocessorEnvironment e,
+      final InternalScanner s) throws IOException {
+    hadPreScannerClose = true;
+  }
+
+  @Override
+  public void postScannerClose(final RegionCoprocessorEnvironment e,
+      final InternalScanner s) throws IOException {
+    hadPostScannerClose = true;
+  }
+
   public boolean wasCompacted() {
     return hadPreCompact && hadPostCompact;
   }
@@ -146,10 +180,7 @@ public class SimpleRegionObserver extend
     assertNotNull(e.getRegion());
     assertNotNull(get);
     assertNotNull(results);
-    if (Arrays.equals(e.getRegion().getTableDesc().getName(),
-        TestRegionObserverInterface.TEST_TABLE)) {
-      hadPreGet = true;
-    }
+    hadPreGet = true;
   }
 
   @Override
@@ -178,8 +209,8 @@ public class SimpleRegionObserver extend
       assertTrue(foundA);
       assertTrue(foundB);
       assertTrue(foundC);
-      hadPostGet = true;
     }
+    hadPostGet = true;
   }
 
   @Override
@@ -205,8 +236,8 @@ public class SimpleRegionObserver extend
       assertNotNull(kvs.get(0));
       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
           TestRegionObserverInterface.C));
-      hadPrePut = true;
     }
+    hadPrePut = true;
   }
 
   @Override
@@ -232,8 +263,8 @@ public class SimpleRegionObserver extend
       assertNotNull(kvs.get(0));
       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
           TestRegionObserverInterface.C));
-      hadPostPut = true;
     }
+    hadPostPut = true;
   }
 
   @Override
@@ -242,8 +273,7 @@ public class SimpleRegionObserver extend
     assertNotNull(e);
     assertNotNull(e.getRegion());
     assertNotNull(familyMap);
-    if (beforeDelete && e.getRegion().getTableDesc().getName().equals(
-        TestRegionObserverInterface.TEST_TABLE)) {
+    if (beforeDelete) {
       hadPreDeleted = true;
     }
   }
@@ -254,11 +284,8 @@ public class SimpleRegionObserver extend
     assertNotNull(e);
     assertNotNull(e.getRegion());
     assertNotNull(familyMap);
-    if (Arrays.equals(e.getRegion().getTableDesc().getName(),
-        TestRegionObserverInterface.TEST_TABLE)) {
-      beforeDelete = false;
-      hadPostDeleted = true;
-    }
+    beforeDelete = false;
+    hadPostDeleted = true;
   }
 
   @Override
@@ -269,8 +296,7 @@ public class SimpleRegionObserver extend
     assertNotNull(e.getRegion());
     assertNotNull(row);
     assertNotNull(result);
-    if (beforeDelete && e.getRegion().getTableDesc().getName().equals(
-        TestRegionObserverInterface.TEST_TABLE)) {
+    if (beforeDelete) {
       hadPreGetClosestRowBefore = true;
     }
   }
@@ -283,63 +309,59 @@ public class SimpleRegionObserver extend
     assertNotNull(e.getRegion());
     assertNotNull(row);
     assertNotNull(result);
-    if (Arrays.equals(e.getRegion().getTableDesc().getName(),
-        TestRegionObserverInterface.TEST_TABLE)) {
-      hadPostGetClosestRowBefore = true;
-    }
+    hadPostGetClosestRowBefore = true;
   }
 
   @Override
   public void preIncrement(final RegionCoprocessorEnvironment e,
       final Increment increment, final Result result) throws IOException {
-    if (Arrays.equals(e.getRegion().getTableDesc().getName(),
-        TestRegionObserverInterface.TEST_TABLE_2)) {
-      hadPreIncrement = true;
-    }
+    hadPreIncrement = true;
   }
 
   @Override
   public void postIncrement(final RegionCoprocessorEnvironment e,
       final Increment increment, final Result result) throws IOException {
-    if (Arrays.equals(e.getRegion().getTableDesc().getName(),
-        TestRegionObserverInterface.TEST_TABLE_2)) {
-      hadPostIncrement = true;
-    }
+    hadPostIncrement = true;
   }
 
-  boolean hadPreGet() {
+  public boolean hadPreGet() {
     return hadPreGet;
   }
 
-  boolean hadPostGet() {
+  public boolean hadPostGet() {
     return hadPostGet;
   }
 
-  boolean hadPrePut() {
+  public boolean hadPrePut() {
     return hadPrePut;
   }
 
-  boolean hadPostPut() {
+  public boolean hadPostPut() {
     return hadPostPut;
   }
-
-  boolean hadDelete() {
+  public boolean hadDelete() {
     return !beforeDelete;
   }
 
-  boolean hadPreIncrement() {
+  public boolean hadPreIncrement() {
     return hadPreIncrement;
   }
 
-  boolean hadPostIncrement() {
+  public boolean hadPostIncrement() {
     return hadPostIncrement;
   }
 
-  boolean hadPreWALRestored() {
+  public boolean hadPreWALRestored() {
     return hadPreWALRestored;
   }
 
-  boolean hadPostWALRestored() {
+  public boolean hadPostWALRestored() {
     return hadPostWALRestored;
   }
+  public boolean wasScannerNextCalled() {
+    return hadPreScannerNext && hadPostScannerNext;
+  }
+  public boolean wasScannerCloseCalled() {
+    return hadPreScannerClose && hadPostScannerClose;
+  }
 }

Modified: hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java?rev=1084854&r1=1084853&r2=1084854&view=diff
==============================================================================
--- hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java (original)
+++ hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java Thu Mar 24 06:27:23 2011
@@ -21,6 +21,7 @@
 package org.apache.hadoop.hbase.coprocessor;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.util.Arrays;
 
 import org.apache.commons.logging.Log;
@@ -32,11 +33,7 @@ import org.apache.hadoop.hbase.HColumnDe
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.MiniHBaseCluster;
-import org.apache.hadoop.hbase.client.Delete;
-import org.apache.hadoop.hbase.client.Get;
-import org.apache.hadoop.hbase.client.HTable;
-import org.apache.hadoop.hbase.client.Increment;
-import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.*;
 import org.apache.hadoop.hbase.coprocessor.Coprocessor.Priority;
 import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
 import org.apache.hadoop.hbase.regionserver.HRegion;
@@ -47,6 +44,8 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import javax.el.MethodNotFoundException;
+
 import static org.junit.Assert.*;
 
 public class TestRegionObserverInterface {
@@ -54,16 +53,10 @@ public class TestRegionObserverInterface
   static final String DIR = "test/build/data/TestRegionObserver/";
 
   public static final byte[] TEST_TABLE = Bytes.toBytes("TestTable");
-  public static final byte[] TEST_TABLE_2 = Bytes.toBytes("TestTable2");
-  public static final byte[] TEST_FAMILY = Bytes.toBytes("TestFamily");
-  public static final byte[] TEST_QUALIFIER = Bytes.toBytes("TestQualifier");
-
   public final static byte[] A = Bytes.toBytes("a");
   public final static byte[] B = Bytes.toBytes("b");
   public final static byte[] C = Bytes.toBytes("c");
   public final static byte[] ROW = Bytes.toBytes("testrow");
-  public final static byte[] ROW1 = Bytes.toBytes("testrow1");
-  public final static byte[] ROW2 = Bytes.toBytes("testrow2");
 
   private static final int ROWSIZE = 20;
   private static byte [][] ROWS = makeN(ROW, ROWSIZE);
@@ -80,17 +73,6 @@ public class TestRegionObserverInterface
 
     util.startMiniCluster(2);
     cluster = util.getMiniHBaseCluster();
-
-    HTable table = util.createTable(TEST_TABLE_2, TEST_FAMILY);
-
-    for(int i = 0; i < ROWSIZE; i++) {
-      Put put = new Put(ROWS[i]);
-      put.add(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(i));
-      table.put(put);
-    }
-
-    // sleep here is an ugly hack to allow region transitions to finish
-    Thread.sleep(5000);
   }
 
   @AfterClass
@@ -98,87 +80,157 @@ public class TestRegionObserverInterface
     util.shutdownMiniCluster();
   }
 
-  HRegion initHRegion (byte [] tableName, String callingMethod,
-      Configuration conf, Class<?> implClass, byte [] ... families)
-      throws IOException{
-    HTableDescriptor htd = new HTableDescriptor(tableName);
-    for(byte [] family : families) {
-      htd.addFamily(new HColumnDescriptor(family));
-    }
-    HRegionInfo info = new HRegionInfo(htd, null, null, false);
-    Path path = new Path(DIR + callingMethod);
-    // this following piece is a hack. currently a coprocessorHost
-    // is secretly loaded at OpenRegionHandler. we don't really
-    // start a region server here, so just manually create cphost
-    // and set it to region.
-    HRegion r = HRegion.createHRegion(info, path, conf);
-    RegionCoprocessorHost host = new RegionCoprocessorHost(r, null, conf);
-    r.setCoprocessorHost(host);
-    host.load(implClass, Priority.USER);
-    return r;
-  }
-
   @Test
   public void testRegionObserver() throws IOException {
-    byte[] TABLE = Bytes.toBytes(getClass().getName());
-    byte[][] FAMILIES = new byte[][] { A, B, C } ;
+    byte[] tableName = TEST_TABLE;
+    // recreate table every time in order to reset the status of the
+    // coproccessor.
+    HTable table = util.createTable(tableName, new byte[][] {A, B, C});
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
+            "hadDelete"},
+        TEST_TABLE,
+        new Boolean[] {false, false, false, false, false});
 
     Put put = new Put(ROW);
     put.add(A, A, A);
     put.add(B, B, B);
     put.add(C, C, C);
+    table.put(put);
+
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
+            "hadDelete"},
+        TEST_TABLE,
+        new Boolean[] {false, false, true, true, false}
+    );
 
     Get get = new Get(ROW);
     get.addColumn(A, A);
     get.addColumn(B, B);
     get.addColumn(C, C);
+    table.get(get);
+
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
+            "hadDelete"},
+        TEST_TABLE,
+        new Boolean[] {true, true, true, true, false}
+    );
 
     Delete delete = new Delete(ROW);
     delete.deleteColumn(A, A);
     delete.deleteColumn(B, B);
     delete.deleteColumn(C, C);
+    table.delete(delete);
 
-    for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) {
-      for (HRegionInfo r : t.getRegionServer().getOnlineRegions()) {
-        if (!Arrays.equals(r.getTableDesc().getName(), TEST_TABLE)) {
-          continue;
-        }
-        RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()).
-          getCoprocessorHost();
-        Coprocessor c = cph.findCoprocessor(SimpleRegionObserver.class.getName());
-        assertNotNull(c);
-        assertTrue(((SimpleRegionObserver)c).hadPreGet());
-        assertTrue(((SimpleRegionObserver)c).hadPostGet());
-        assertTrue(((SimpleRegionObserver)c).hadPrePut());
-        assertTrue(((SimpleRegionObserver)c).hadPostPut());
-        assertTrue(((SimpleRegionObserver)c).hadDelete());
-      }
-    }
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
+            "hadDelete"},
+        TEST_TABLE,
+        new Boolean[] {true, true, true, true, true}
+    );
+    util.deleteTable(tableName);
   }
 
-  // TODO: add tests for other methods which need to be tested
-  // at region servers.
-
   @Test
   public void testIncrementHook() throws IOException {
-    HTable table = new HTable(util.getConfiguration(), TEST_TABLE_2);
+    byte[] tableName = TEST_TABLE;
 
+    HTable table = util.createTable(tableName, new byte[][] {A, B, C});
     Increment inc = new Increment(Bytes.toBytes(0));
-    inc.addColumn(TEST_FAMILY, TEST_QUALIFIER, 1);
+    inc.addColumn(A, A, 1);
+
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreIncrement", "hadPostIncrement"},
+        tableName,
+        new Boolean[] {false, false}
+    );
 
     table.increment(inc);
 
-    for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) {
-      for (HRegionInfo r : t.getRegionServer().getOnlineRegions()) {
-        if (!Arrays.equals(r.getTableDesc().getName(), TEST_TABLE_2)) {
-          continue;
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreIncrement", "hadPostIncrement"},
+        tableName,
+        new Boolean[] {true, true}
+    );
+    util.deleteTable(tableName);
+  }
+
+  @Test
+  // HBase-3583
+  public void testHBase3583() throws IOException {
+    byte[] tableName = Bytes.toBytes("testHBase3583");
+    util.createTable(tableName, new byte[][] {A, B, C});
+
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreGet", "hadPostGet", "wasScannerNextCalled",
+            "wasScannerCloseCalled"},
+        tableName,
+        new Boolean[] {false, false, false, false}
+    );
+
+    HTable table = new HTable(util.getConfiguration(), tableName);
+    Put put = new Put(ROW);
+    put.add(A, A, A);
+    table.put(put);
+
+    Get get = new Get(ROW);
+    get.addColumn(A, A);
+    table.get(get);
+
+    // verify that scannerNext and scannerClose upcalls won't be invoked
+    // when we perform get().
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"hadPreGet", "hadPostGet", "wasScannerNextCalled",
+            "wasScannerCloseCalled"},
+        tableName,
+        new Boolean[] {true, true, false, false}
+    );
+
+    Scan s = new Scan();
+    ResultScanner scanner = table.getScanner(s);
+    try {
+      for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
+      }
+    } finally {
+      scanner.close();
+    }
+
+    // now scanner hooks should be invoked.
+    verifyMethodResult(SimpleRegionObserver.class,
+        new String[] {"wasScannerNextCalled", "wasScannerCloseCalled"},
+        tableName,
+        new Boolean[] {true, true}
+    );
+    util.deleteTable(tableName);
+  }
+
+  // check each region whether the coprocessor upcalls are called or not.
+  private void verifyMethodResult(Class c, String methodName[], byte[] tableName,
+                                  Object value[]) throws IOException {
+    try {
+      for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) {
+        for (HRegionInfo r : t.getRegionServer().getOnlineRegions()) {
+          if (!Arrays.equals(r.getTableDesc().getName(), tableName)) {
+            continue;
+          }
+          RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()).
+              getCoprocessorHost();
+
+          Coprocessor cp = cph.findCoprocessor(c.getName());
+          assertNotNull(cp);
+          for (int i = 0; i < methodName.length; ++i) {
+            Method m = c.getMethod(methodName[i]);
+            Object o = m.invoke(cp);
+            assertTrue("Result of " + c.getName() + "." + methodName[i]
+                + " is expected to be " + value[i].toString()
+                + ", while we get " + o.toString(), o.equals(value[i]));
+          }
         }
-        RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()).
-          getCoprocessorHost();
-        Coprocessor c = cph.findCoprocessor(SimpleRegionObserver.class.getName());
-        assertTrue(((SimpleRegionObserver)c).hadPreIncrement());
-        assertTrue(((SimpleRegionObserver)c).hadPostIncrement());
       }
+    } catch (Exception e) {
+      throw new IOException(e.toString());
     }
   }