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/05/24 22:35:51 UTC

svn commit: r1486211 - in /hbase/branches/0.95/hbase-server/src: main/java/org/apache/hadoop/hbase/regionserver/wal/ test/java/org/apache/hadoop/hbase/ test/java/org/apache/hadoop/hbase/regionserver/wal/

Author: tedyu
Date: Fri May 24 20:35:51 2013
New Revision: 1486211

URL: http://svn.apache.org/r1486211
Log:
HBASE-8597 compaction record (probably) can block WAL cleanup forever if region is closed without edits (Sergey)


Modified:
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLog.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLogUtil.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestLogRolling.java

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java?rev=1486211&r1=1486210&r2=1486211&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java Fri May 24 20:35:51 2013
@@ -832,7 +832,13 @@ class FSHLog implements HLog, Syncable {
   public void append(HRegionInfo info, byte [] tableName, WALEdit edits,
     final long now, HTableDescriptor htd)
   throws IOException {
-    append(info, tableName, edits, HConstants.DEFAULT_CLUSTER_ID, now, htd);
+    append(info, tableName, edits, now, htd, true);
+  }
+
+  @Override
+  public void append(HRegionInfo info, byte [] tableName, WALEdit edits,
+    final long now, HTableDescriptor htd, boolean isInMemstore) throws IOException {
+    append(info, tableName, edits, HConstants.DEFAULT_CLUSTER_ID, now, htd, true, isInMemstore);
   }
 
   /**
@@ -862,9 +868,9 @@ class FSHLog implements HLog, Syncable {
    * @throws IOException
    */
   private long append(HRegionInfo info, byte [] tableName, WALEdit edits, UUID clusterId,
-      final long now, HTableDescriptor htd, boolean doSync)
+      final long now, HTableDescriptor htd, boolean doSync, boolean isInMemstore)
     throws IOException {
-      if (edits.isEmpty()) return this.unflushedEntries.get();;
+      if (edits.isEmpty()) return this.unflushedEntries.get();
       if (this.closed) {
         throw new IOException("Cannot append; log is closed");
       }
@@ -879,7 +885,7 @@ class FSHLog implements HLog, Syncable {
         // Use encoded name.  Its shorter, guaranteed unique and a subset of
         // actual  name.
         byte [] encodedRegionName = info.getEncodedNameAsBytes();
-        this.oldestUnflushedSeqNums.putIfAbsent(encodedRegionName, seqNum);
+        if (isInMemstore) this.oldestUnflushedSeqNums.putIfAbsent(encodedRegionName, seqNum);
         HLogKey logKey = makeKey(encodedRegionName, tableName, seqNum, now, clusterId);
         doWrite(info, logKey, edits, htd);
         this.numEntries.incrementAndGet();
@@ -903,14 +909,7 @@ class FSHLog implements HLog, Syncable {
   public long appendNoSync(HRegionInfo info, byte [] tableName, WALEdit edits,
     UUID clusterId, final long now, HTableDescriptor htd)
     throws IOException {
-    return append(info, tableName, edits, clusterId, now, htd, false);
-  }
-
-  @Override
-  public long append(HRegionInfo info, byte [] tableName, WALEdit edits,
-    UUID clusterId, final long now, HTableDescriptor htd)
-    throws IOException {
-    return append(info, tableName, edits, clusterId, now, htd, true);
+    return append(info, tableName, edits, clusterId, now, htd, false, true);
   }
 
   /**

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLog.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLog.java?rev=1486211&r1=1486210&r2=1486211&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLog.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLog.java Fri May 24 20:35:51 2013
@@ -264,39 +264,30 @@ public interface HLog {
   public void closeAndDelete() throws IOException;
 
   /**
-   * Only used in tests.
-   *
-   * @param info
-   * @param tableName
-   * @param edits
-   * @param now
-   * @param htd
-   * @throws IOException
+   * Same as {@link #appendNoSync(HRegionInfo, byte[], WALEdit, UUID, long, HTableDescriptor)},
+   * except it causes a sync on the log
    */
   public void append(HRegionInfo info, byte[] tableName, WALEdit edits,
       final long now, HTableDescriptor htd) throws IOException;
 
   /**
    * Append a set of edits to the log. Log edits are keyed by (encoded)
-   * regionName, rowname, and log-sequence-id. The HLog is not flushed after
-   * this transaction is written to the log.
-   *
+   * regionName, rowname, and log-sequence-id. The HLog is flushed after this
+   * transaction is written to the log.
    * @param info
    * @param tableName
    * @param edits
-   * @param clusterId
-   *          The originating clusterId for this edit (for replication)
    * @param now
-   * @return txid of this transaction
-   * @throws IOException
+   * @param htd
+   * @param isInMemstore Whether the record is in memstore. False for system records.
    */
-  public long appendNoSync(HRegionInfo info, byte[] tableName, WALEdit edits,
-      UUID clusterId, final long now, HTableDescriptor htd) throws IOException;
+  public void append(HRegionInfo info, byte[] tableName, WALEdit edits,
+      final long now, HTableDescriptor htd, boolean isInMemstore) throws IOException;
 
   /**
    * Append a set of edits to the log. Log edits are keyed by (encoded)
-   * regionName, rowname, and log-sequence-id. The HLog is flushed after this
-   * transaction is written to the log.
+   * regionName, rowname, and log-sequence-id. The HLog is not flushed after
+   * this transaction is written to the log.
    *
    * @param info
    * @param tableName
@@ -308,7 +299,7 @@ public interface HLog {
    * @return txid of this transaction
    * @throws IOException
    */
-  public long append(HRegionInfo info, byte[] tableName, WALEdit edits,
+  public long appendNoSync(HRegionInfo info, byte[] tableName, WALEdit edits,
       UUID clusterId, final long now, HTableDescriptor htd) throws IOException;
 
   public void hsync() throws IOException;

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLogUtil.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLogUtil.java?rev=1486211&r1=1486210&r2=1486211&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLogUtil.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/HLogUtil.java Fri May 24 20:35:51 2013
@@ -262,7 +262,7 @@ public class HLogUtil {
       final CompactionDescriptor c) throws IOException {
     WALEdit e = WALEdit.createCompaction(c);
     log.append(info, c.getTableName().toByteArray(), e,
-        EnvironmentEdgeManager.currentTimeMillis(), htd);
+        EnvironmentEdgeManager.currentTimeMillis(), htd, false);
     if (LOG.isTraceEnabled()) {
       LOG.trace("Appended compaction marker " + TextFormat.shortDebugString(c));
     }

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java?rev=1486211&r1=1486210&r2=1486211&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java Fri May 24 20:35:51 2013
@@ -734,7 +734,16 @@ public class HBaseTestingUtility extends
    */
   public MiniHBaseCluster startMiniCluster(final int numMasters,
       final int numSlaves, final String[] dataNodeHosts) throws Exception {
-    return startMiniCluster(numMasters, numSlaves, dataNodeHosts, null, null);
+    return startMiniCluster(numMasters, numSlaves, numSlaves, dataNodeHosts, null, null);
+  }
+
+  /**
+   * Same as {@link #startMiniCluster(int, int)}, but with custom number of datanodes.
+   * @param numDataNodes Number of data nodes.
+   */
+  public MiniHBaseCluster startMiniCluster(final int numMasters,
+      final int numSlaves, final int numDataNodes) throws Exception {
+    return startMiniCluster(numMasters, numSlaves, numDataNodes, null, null, null);
   }
 
   /**
@@ -765,12 +774,24 @@ public class HBaseTestingUtility extends
    * @return Mini hbase cluster instance created.
    */
   public MiniHBaseCluster startMiniCluster(final int numMasters,
-    final int numSlaves, final String[] dataNodeHosts,
+      final int numSlaves, final String[] dataNodeHosts, Class<? extends HMaster> masterClass,
+      Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
+          throws Exception {
+    return startMiniCluster(
+        numMasters, numSlaves, numSlaves, dataNodeHosts, masterClass, regionserverClass);
+  }
+
+  /**
+   * Same as {@link #startMiniCluster(int, int, String[], Class, Class)}, but with custom
+   * number of datanodes.
+   * @param numDataNodes Number of data nodes.
+   */
+  public MiniHBaseCluster startMiniCluster(final int numMasters,
+    final int numSlaves, int numDataNodes, final String[] dataNodeHosts,
     Class<? extends HMaster> masterClass,
     Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
   throws Exception {
-    int numDataNodes = numSlaves;
-    if ( dataNodeHosts != null && dataNodeHosts.length != 0) {
+    if (dataNodeHosts != null && dataNodeHosts.length != 0) {
       numDataNodes = dataNodeHosts.length;
     }
 

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestLogRolling.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestLogRolling.java?rev=1486211&r1=1486210&r2=1486211&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestLogRolling.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestLogRolling.java Fri May 24 20:35:51 2013
@@ -57,9 +57,11 @@ import org.apache.hadoop.hbase.exception
 import org.apache.hadoop.hbase.fs.HFileSystem;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.HRegionServer;
+import org.apache.hadoop.hbase.regionserver.Store;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.FSUtils;
 import org.apache.hadoop.hbase.util.JVMClusterUtil;
+import org.apache.hadoop.hbase.util.Threads;
 import org.apache.hadoop.hdfs.DFSClient;
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
@@ -67,6 +69,7 @@ import org.apache.hadoop.hdfs.server.dat
 import org.apache.hadoop.hdfs.server.namenode.LeaseManager;
 import org.apache.log4j.Level;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -170,7 +173,7 @@ public class TestLogRolling  {
 
   @Before
   public void setUp() throws Exception {
-    TEST_UTIL.startMiniCluster(2);
+    TEST_UTIL.startMiniCluster(1, 1, 2);
 
     cluster = TEST_UTIL.getHBaseCluster();
     dfsCluster = TEST_UTIL.getDFSCluster();
@@ -192,18 +195,12 @@ public class TestLogRolling  {
     this.server = cluster.getRegionServerThreads().get(0).getRegionServer();
     this.log = server.getWAL();
 
-    // Create the test table and open it
-    HTableDescriptor desc = new HTableDescriptor(tableName);
-    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
-    admin.createTable(desc);
-    HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
+    HTable table = createTestTable(this.tableName);
 
     server = TEST_UTIL.getRSForFirstRegionInTable(Bytes.toBytes(tableName));
     this.log = server.getWAL();
     for (int i = 1; i <= 256; i++) {    // 256 writes should cause 8 log rolls
-      Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", i)));
-      put.add(HConstants.CATALOG_FAMILY, null, value);
-      table.put(put);
+      doPut(table, i);
       if (i % 32 == 0) {
         // After every 32 writes sleep to let the log roller run
         try {
@@ -221,7 +218,7 @@ public class TestLogRolling  {
    * @throws org.apache.hadoop.hbase.exceptions.FailedLogCloseException
    */
   @Test
-  public void testLogRolling() throws FailedLogCloseException, IOException {
+  public void testLogRolling() throws Exception {
     this.tableName = getName();
       startAndWriteData();
       LOG.info("after writing there are " + ((FSHLog) log).getNumLogFiles() + " log files");
@@ -248,9 +245,7 @@ public class TestLogRolling  {
   }
 
   void writeData(HTable table, int rownum) throws IOException {
-    Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", rownum)));
-    put.add(HConstants.CATALOG_FAMILY, null, value);
-    table.put(put);
+    doPut(table, rownum);
 
     // sleep to let the log roller run (if it needs to)
     try {
@@ -324,12 +319,7 @@ public class TestLogRolling  {
   /**
    * Tests that logs are rolled upon detecting datanode death
    * Requires an HDFS jar with HDFS-826 & syncFs() support (HDFS-200)
-   * @throws IOException
-   * @throws InterruptedException
-   * @throws InvocationTargetException
-   * @throws IllegalAccessException
-   * @throws IllegalArgumentException
-    */
+   */
   @Test
   public void testLogRollOnDatanodeDeath() throws Exception {
     assertTrue("This test requires HLog file replication set to 2.",
@@ -587,5 +577,75 @@ public class TestLogRolling  {
     }
   }
 
+  /**
+   * Tests that logs are deleted when some region has a compaction
+   * record in WAL and no other records. See HBASE-8597.
+   */
+  @Test
+  public void testCompactionRecordDoesntBlockRolling() throws Exception {
+    // When the META table can be opened, the region servers are running
+    new HTable(TEST_UTIL.getConfiguration(), HConstants.META_TABLE_NAME);
+
+    String tableName = getName();
+    HTable table = createTestTable(tableName);
+    String tableName2 = tableName + "1";
+    HTable table2 = createTestTable(tableName2);
+
+    server = TEST_UTIL.getRSForFirstRegionInTable(Bytes.toBytes(tableName));
+    this.log = server.getWAL();
+    FSHLog fshLog = (FSHLog)log;
+    HRegion region = server.getOnlineRegions(table2.getTableName()).get(0);
+    Store s = region.getStore(HConstants.CATALOG_FAMILY);
+
+
+    // Put some stuff into table2, to make sure we have some files to compact.
+    for (int i = 1; i <= 2; ++i) {
+      doPut(table2, i);
+      admin.flush(table2.getTableName());
+    }
+    doPut(table2, 3); // don't flush yet, or compaction might trigger before we roll WAL
+    assertEquals("Should have no WAL after initial writes", 0, fshLog.getNumLogFiles());
+    assertEquals(2, s.getStorefilesCount());
+
+    // Roll the log and compact table2, to have compaction record in the 2nd WAL.
+    fshLog.rollWriter();
+    assertEquals("Should have WAL; one table is not flushed", 1, fshLog.getNumLogFiles());
+    admin.flush(table2.getTableName());
+    region.compactStores();
+    // Wait for compaction in case if flush triggered it before us.
+    Assert.assertNotNull(s);
+    for (int waitTime = 3000; s.getStorefilesCount() > 1 && waitTime > 0; waitTime -= 200) {
+      Threads.sleepWithoutInterrupt(200);
+    }
+    assertEquals("Compaction didn't happen", 1, s.getStorefilesCount());
+
+    // Write some value to the table so the WAL cannot be deleted until table is flushed.
+    doPut(table, 0); // Now 2nd WAL will have compaction record for table2 and put for table.
+    fshLog.rollWriter(); // 1st WAL deleted, 2nd not deleted yet.
+    assertEquals("Should have WAL; one table is not flushed", 1, fshLog.getNumLogFiles());
+
+    // Flush table to make latest WAL obsolete; write another record, and roll again.
+    admin.flush(table.getTableName());
+    doPut(table, 1);
+    fshLog.rollWriter(); // Now 2nd WAL is deleted and 3rd is added.
+    assertEquals("Should have 1 WALs at the end", 1, fshLog.getNumLogFiles());
+
+    table.close();
+    table2.close();
+  }
+
+  private void doPut(HTable table, int i) throws IOException {
+    Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", i)));
+    put.add(HConstants.CATALOG_FAMILY, null, value);
+    table.put(put);
+  }
+
+  private HTable createTestTable(String tableName) throws IOException {
+    // Create the test table and open it
+    HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+    admin.createTable(desc);
+    return new HTable(TEST_UTIL.getConfiguration(), tableName);
+  }
 }