diff --git src/main/java/org/apache/hadoop/hbase/HConstants.java src/main/java/org/apache/hadoop/hbase/HConstants.java index 25f5e15..5f4a6c3 100644 --- src/main/java/org/apache/hadoop/hbase/HConstants.java +++ src/main/java/org/apache/hadoop/hbase/HConstants.java @@ -38,6 +38,7 @@ public final class HConstants { public enum OperationStatusCode { NOT_RUN, SUCCESS, + BAD_FAMILY, SANITY_CHECK_FAILURE, FAILURE; } diff --git src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java index e8d2c26..d27bff5 100644 --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java @@ -2065,10 +2065,19 @@ public class HRegion implements HeapSize { // , Writable{ } else { prepareDelete((Delete) mutation); } - } catch (DoNotRetryIOException dnrioe) { - LOG.warn("No such column family in batch mutation", dnrioe); + } catch (NoSuchColumnFamilyException nscf) { + LOG.warn("No such column family in batch mutation", nscf); batchOp.retCodeDetails[lastIndexExclusive] = new OperationStatus( - OperationStatusCode.SANITY_CHECK_FAILURE, dnrioe.getMessage()); + OperationStatusCode.BAD_FAMILY, nscf.getMessage()); + lastIndexExclusive++; + continue; + } catch (DoNotRetryIOException fsce) { + // The only thing that throws a generic DoNotRetryIOException in the above code is + // checkTimestamps so that DoNotRetryIOException means that timestamps were invalid. + // If more checks are added, be sure to revisit this assumption. + LOG.warn("Batch Mutation did not pass sanity check", fsce); + batchOp.retCodeDetails[lastIndexExclusive] = new OperationStatus( + OperationStatusCode.SANITY_CHECK_FAILURE, fsce.getMessage()); lastIndexExclusive++; continue; } diff --git src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index 761c982..1f83263 100644 --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -3464,7 +3464,12 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, result = new Result(); } else if (code.getOperationStatusCode() == OperationStatusCode.SANITY_CHECK_FAILURE) { + // Don't send a FailedSanityCheckException as older clients will not know about + // that class being a subclass of DoNotRetryIOException + // and will retry mutations that will never succeed. result = new DoNotRetryIOException(code.getExceptionMsg()); + } else if (code.getOperationStatusCode() == OperationStatusCode.BAD_FAMILY) { + result = new NoSuchColumnFamilyException(code.getExceptionMsg()); } // FAILURE && NOT_RUN becomes null, aka: need to run again. diff --git src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java index 6ce3fa3..d3cab72 100644 --- src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java +++ src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java @@ -85,6 +85,7 @@ import org.apache.hadoop.hbase.io.hfile.BlockCache; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.HRegionServer; +import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException; import org.apache.hadoop.hbase.regionserver.Store; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Threads; @@ -3615,6 +3616,25 @@ public class TestFromClientSide { } @Test + public void testPutNoCF() throws IOException { + final byte[] BAD_FAM = Bytes.toBytes("BAD_CF"); + final byte[] VAL = Bytes.toBytes(100); + HTable table = TEST_UTIL.createTable(Bytes.toBytes("testPutNoCF"), new byte[][]{FAMILY}); + + boolean caughtNSCFE = false; + + try { + Put p = new Put(ROW); + p.add(BAD_FAM, QUALIFIER, VAL); + table.put(p); + } catch (RetriesExhaustedWithDetailsException e) { + caughtNSCFE = e.getCause(0) instanceof NoSuchColumnFamilyException; + } + assertTrue("Should throw NoSuchColumnFamilyException", caughtNSCFE); + + } + + @Test public void testRowsPut() throws IOException { final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents"); final byte[] SMALL_FAMILY = Bytes.toBytes("smallfam"); @@ -4165,6 +4185,8 @@ public class TestFromClientSide { } } + + @Test public void testIncrement() throws Exception { LOG.info("Starting testIncrement"); diff --git src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java index 081d868..21f3ded 100644 --- src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java +++ src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java @@ -558,7 +558,7 @@ public class TestHRegion extends HBaseTestCase { boolean exception = false; try { this.region.put(p); - } catch (DoNotRetryIOException e) { + } catch (NoSuchColumnFamilyException e) { exception = true; } assertTrue(exception); @@ -599,7 +599,7 @@ public class TestHRegion extends HBaseTestCase { codes = this.region.put(puts); assertEquals(10, codes.length); for (int i = 0; i < 10; i++) { - assertEquals((i == 5) ? OperationStatusCode.SANITY_CHECK_FAILURE : + assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode()); } assertEquals(1, HLog.getSyncTime().count); @@ -637,7 +637,7 @@ public class TestHRegion extends HBaseTestCase { assertEquals(1, HLog.getSyncTime().count); codes = retFromThread.get(); for (int i = 0; i < 10; i++) { - assertEquals((i == 5) ? OperationStatusCode.SANITY_CHECK_FAILURE : + assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode()); } @@ -654,7 +654,7 @@ public class TestHRegion extends HBaseTestCase { codes = region.put(putsAndLocks.toArray(new Pair[0])); LOG.info("...performed put"); for (int i = 0; i < 10; i++) { - assertEquals((i == 5) ? OperationStatusCode.SANITY_CHECK_FAILURE : + assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode()); } // Make sure we didn't do an extra batch @@ -670,6 +670,45 @@ public class TestHRegion extends HBaseTestCase { } } + public void testBatchPutWithTsSlop() throws Exception { + byte[] b = Bytes.toBytes(getName()); + byte[] cf = Bytes.toBytes(COLUMN_FAMILY); + byte[] qual = Bytes.toBytes("qual"); + byte[] val = Bytes.toBytes("val"); + + HBaseConfiguration conf = new HBaseConfiguration(); + + + // add data with a timestamp that is too recent for range. Ensure assert + conf.setInt("hbase.hregion.keyvalue.timestamp.slop.millisecs", 1000); + this.region = initHRegion(b, getName(), conf, cf); + + try{ + HLog.getSyncTime(); // clear counter from prior tests + assertEquals(0, HLog.getSyncTime().count); + + final Put[] puts = new Put[10]; + for (int i = 0; i < 10; i++) { + puts[i] = new Put(Bytes.toBytes("row_" + i), Long.MAX_VALUE - 100); + puts[i].add(cf, qual, val); + } + + OperationStatus[] codes = this.region.put(puts); + assertEquals(10, codes.length); + for (int i = 0; i < 10; i++) { + assertEquals(OperationStatusCode.SANITY_CHECK_FAILURE, codes[i] + .getOperationStatusCode()); + } + assertEquals(0, HLog.getSyncTime().count); + + + } finally { + HRegion.closeHRegion(this.region); + this.region = null; + } + + } + ////////////////////////////////////////////////////////////////////////////// // checkAndMutate tests ////////////////////////////////////////////////////////////////////////////// @@ -1202,6 +1241,7 @@ public class TestHRegion extends HBaseTestCase { } + /** * Tests that there is server-side filtering for invalid timestamp upper * bound. Note that the timestamp lower bound is automatically handled for us @@ -1217,6 +1257,7 @@ public class TestHRegion extends HBaseTestCase { // add data with a timestamp that is too recent for range. Ensure assert conf.setInt("hbase.hregion.keyvalue.timestamp.slop.millisecs", 1000); this.region = initHRegion(tableName, method, conf, families); + boolean caughtExcep = false; try { try { // no TS specified == use latest. should not error @@ -1229,7 +1270,9 @@ public class TestHRegion extends HBaseTestCase { fail("Expected IOE for TS out of configured timerange"); } catch (DoNotRetryIOException ioe) { LOG.debug("Received expected exception", ioe); + caughtExcep = true; } + assertTrue("Should catch FailedSanityCheckException", caughtExcep); } finally { HRegion.closeHRegion(this.region); this.region = null;