You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by nd...@apache.org on 2024/09/30 17:21:36 UTC

(hbase) branch branch-2.6 updated: HBASE-28803 HBase Master stuck due to improper handling of WALSyncTimeoutException within UncheckedIOException (#6254)

This is an automated email from the ASF dual-hosted git repository.

ndimiduk pushed a commit to branch branch-2.6
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-2.6 by this push:
     new 1d8b14014bd HBASE-28803 HBase Master stuck due to improper handling of WALSyncTimeoutException within UncheckedIOException (#6254)
1d8b14014bd is described below

commit 1d8b14014bda6da4cd3bf0df32feffa9178aa3ec
Author: Nick Dimiduk <nd...@apache.org>
AuthorDate: Mon Sep 30 19:21:30 2024 +0200

    HBASE-28803 HBase Master stuck due to improper handling of WALSyncTimeoutException within UncheckedIOException (#6254)
    
    Signed-off-by: Peter Somogyi <ps...@apache.org>
    Signed-off-by: Ray Mattingly <rm...@apache.org>
---
 .../hadoop/hbase/master/region/MasterRegion.java   |  33 ++++--
 .../TestMasterRegionWALSyncTimeoutIOException.java | 120 +++++++++++++++++++++
 2 files changed, 145 insertions(+), 8 deletions(-)

diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/region/MasterRegion.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/region/MasterRegion.java
index 2c0c98d86f2..119624dd635 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/region/MasterRegion.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/region/MasterRegion.java
@@ -38,6 +38,7 @@ import org.apache.hadoop.hbase.client.ResultScanner;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
+import org.apache.hadoop.hbase.log.HBaseMarkers;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.HRegion.FlushResult;
 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
@@ -46,6 +47,7 @@ import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
 import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;
 import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
 import org.apache.hadoop.hbase.regionserver.wal.AbstractFSWAL;
+import org.apache.hadoop.hbase.regionserver.wal.WALSyncTimeoutIOException;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.CommonFSUtils;
 import org.apache.hadoop.hbase.util.FSTableDescriptors;
@@ -106,6 +108,8 @@ public final class MasterRegion {
 
   private static final int REGION_ID = 1;
 
+  private final Server server;
+
   private final WALFactory walFactory;
 
   final HRegion region;
@@ -114,8 +118,9 @@ public final class MasterRegion {
 
   private MasterRegionWALRoller walRoller;
 
-  private MasterRegion(HRegion region, WALFactory walFactory,
+  private MasterRegion(Server server, HRegion region, WALFactory walFactory,
     MasterRegionFlusherAndCompactor flusherAndCompactor, MasterRegionWALRoller walRoller) {
+    this.server = server;
     this.region = region;
     this.walFactory = walFactory;
     this.flusherAndCompactor = flusherAndCompactor;
@@ -139,8 +144,14 @@ public final class MasterRegion {
   }
 
   public void update(UpdateMasterRegion action) throws IOException {
-    action.update(region);
-    flusherAndCompactor.onUpdate();
+    try {
+      action.update(region);
+      flusherAndCompactor.onUpdate();
+    } catch (WALSyncTimeoutIOException e) {
+      LOG.error(HBaseMarkers.FATAL, "WAL sync timeout. Aborting server.");
+      server.abort("WAL sync timeout", e);
+      throw e;
+    }
   }
 
   public Result get(Get get) throws IOException {
@@ -156,10 +167,16 @@ public final class MasterRegion {
   }
 
   public FlushResult flush(boolean force) throws IOException {
-    flusherAndCompactor.resetChangesAfterLastFlush();
-    FlushResult flushResult = region.flush(force);
-    flusherAndCompactor.recordLastFlushTime();
-    return flushResult;
+    try {
+      flusherAndCompactor.resetChangesAfterLastFlush();
+      FlushResult flushResult = region.flush(force);
+      flusherAndCompactor.recordLastFlushTime();
+      return flushResult;
+    } catch (WALSyncTimeoutIOException e) {
+      LOG.error(HBaseMarkers.FATAL, "WAL sync timeout. Aborting server.");
+      server.abort("WAL sync timeout", e);
+      throw e;
+    }
   }
 
   @RestrictedApi(explanation = "Should only be called in tests", link = "",
@@ -446,6 +463,6 @@ public final class MasterRegion {
       LOG.warn("Failed to create archive directory {}. Usually this should not happen but it will"
         + " be created again when we actually archive the hfiles later, so continue", archiveDir);
     }
-    return new MasterRegion(region, walFactory, flusherAndCompactor, walRoller);
+    return new MasterRegion(server, region, walFactory, flusherAndCompactor, walRoller);
   }
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/region/TestMasterRegionWALSyncTimeoutIOException.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/region/TestMasterRegionWALSyncTimeoutIOException.java
new file mode 100644
index 00000000000..810135b8370
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/region/TestMasterRegionWALSyncTimeoutIOException.java
@@ -0,0 +1,120 @@
+/*
+ * 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.master.region;
+
+import static org.junit.Assert.assertThrows;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.List;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.io.asyncfs.monitor.StreamSlowMonitor;
+import org.apache.hadoop.hbase.regionserver.wal.AbstractFSWAL;
+import org.apache.hadoop.hbase.regionserver.wal.AsyncFSWAL;
+import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
+import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
+import org.apache.hadoop.hbase.regionserver.wal.WALSyncTimeoutIOException;
+import org.apache.hadoop.hbase.testclassification.MasterTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
+import org.apache.hadoop.hbase.wal.AsyncFSWALProvider;
+import org.apache.hadoop.hbase.wal.WALFactory;
+import org.apache.hadoop.hbase.wal.WALProvider;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.hbase.thirdparty.io.netty.channel.Channel;
+import org.apache.hbase.thirdparty.io.netty.channel.EventLoopGroup;
+
+@Category({ MasterTests.class, MediumTests.class })
+public class TestMasterRegionWALSyncTimeoutIOException extends MasterRegionTestBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestMasterRegionWALSyncTimeoutIOException.class);
+
+  private static final Duration WAL_SYNC_TIMEOUT = Duration.ofSeconds(3);
+
+  private static volatile boolean testWalTimeout = false;
+
+  @Override
+  protected void configure(Configuration conf) throws IOException {
+    conf.setClass(WALFactory.WAL_PROVIDER, SlowAsyncFSWALProvider.class, WALProvider.class);
+    conf.setLong(AbstractFSWAL.WAL_SYNC_TIMEOUT_MS, WAL_SYNC_TIMEOUT.toMillis());
+  }
+
+  @Override
+  protected void configure(MasterRegionParams params) {
+    params.flushIntervalMs(Duration.ofSeconds(1).toMillis());
+  }
+
+  @Test
+  public void testUpdateWalSyncWriteException() {
+    testWalTimeout = true;
+    assertThrows(WALSyncTimeoutIOException.class, () -> {
+      for (int i = 0; i < 10; i++) {
+        region.update(
+          r -> r.put(new Put(Bytes.toBytes("0")).addColumn(CF1, QUALIFIER, Bytes.toBytes("0"))));
+        Thread.sleep(Duration.ofSeconds(1).toMillis());
+      }
+    });
+  }
+
+  public static class SlowAsyncFSWAL extends AsyncFSWAL {
+
+    public SlowAsyncFSWAL(FileSystem fs, Abortable abortable, Path rootDir, String logDir,
+      String archiveDir, Configuration conf, List<WALActionsListener> listeners,
+      boolean failIfWALExists, String prefix, String suffix, EventLoopGroup eventLoopGroup,
+      Class<? extends Channel> channelClass, StreamSlowMonitor monitor)
+      throws FailedLogCloseException, IOException {
+      super(fs, abortable, rootDir, logDir, archiveDir, conf, listeners, failIfWALExists, prefix,
+        suffix, eventLoopGroup, channelClass, monitor);
+    }
+
+    @Override
+    protected void atHeadOfRingBufferEventHandlerAppend() {
+      if (testWalTimeout) {
+        try {
+          Thread.sleep(WAL_SYNC_TIMEOUT.plusSeconds(1).toMillis());
+        } catch (InterruptedException e) {
+          throw new RuntimeException(e);
+        }
+      }
+      super.atHeadOfRingBufferEventHandlerAppend();
+    }
+  }
+
+  public static class SlowAsyncFSWALProvider extends AsyncFSWALProvider {
+
+    @Override
+    protected AsyncFSWAL createWAL() throws IOException {
+      return new SlowAsyncFSWAL(CommonFSUtils.getWALFileSystem(conf), this.abortable,
+        CommonFSUtils.getWALRootDir(conf), getWALDirectoryName(factory.getFactoryId()),
+        getWALArchiveDirectoryName(conf, factory.getFactoryId()), conf, listeners, true, logPrefix,
+        META_WAL_PROVIDER_ID.equals(providerId) ? META_WAL_PROVIDER_ID : null, eventLoopGroup,
+        channelClass, factory.getExcludeDatanodeManager().getStreamSlowMonitor(providerId));
+    }
+  }
+}