001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 * <p>
010 * http://www.apache.org/licenses/LICENSE-2.0
011 * <p>
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver.wal;
019
020import java.io.IOException;
021import java.util.List;
022
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.fs.FileStatus;
025import org.apache.hadoop.fs.FileSystem;
026import org.apache.hadoop.fs.Path;
027import org.apache.hadoop.hbase.FailedCloseWALAfterInitializedErrorException;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.HRegionInfo;
031import org.apache.hadoop.hbase.ServerName;
032import org.apache.hadoop.hbase.testclassification.MediumTests;
033import org.apache.hadoop.hbase.testclassification.RegionServerTests;
034import org.apache.hadoop.hbase.util.CommonFSUtils;
035import org.apache.hadoop.hbase.wal.FSHLogProvider;
036import org.apache.hadoop.hbase.wal.WALFactory;
037import org.apache.hadoop.hdfs.MiniDFSCluster;
038
039import org.junit.After;
040import org.junit.AfterClass;
041import org.junit.Assert;
042import org.junit.Before;
043import org.junit.BeforeClass;
044import org.junit.ClassRule;
045import org.junit.Rule;
046import org.junit.Test;
047import org.junit.experimental.categories.Category;
048import org.junit.rules.TestName;
049import org.slf4j.Logger;
050import org.slf4j.LoggerFactory;
051
052/**
053 * Test WAL Init ERROR
054 */
055@Category({RegionServerTests.class, MediumTests.class})
056public class TestWALOpenError {
057
058  @ClassRule
059  public static final HBaseClassTestRule CLASS_RULE =
060    HBaseClassTestRule.forClass(TestWALOpenError.class);
061
062  private static final Logger LOG = LoggerFactory.getLogger(TestWALOpenError.class);
063
064  protected static Configuration conf;
065  private static MiniDFSCluster cluster;
066  protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
067  protected static Path hbaseDir;
068  protected static Path hbaseWALDir;
069
070  protected FileSystem fs;
071  protected Path dir;
072  protected WALFactory wals;
073  private ServerName currentServername;
074
075  @Rule
076  public final TestName currentTest = new TestName();
077
078  @Before
079  public void setUp() throws Exception {
080    fs = cluster.getFileSystem();
081    dir = new Path(hbaseDir, currentTest.getMethodName());
082    this.currentServername = ServerName.valueOf(currentTest.getMethodName(), 16010, 1);
083    wals = new WALFactory(conf, this.currentServername.toString());
084  }
085
086  @After
087  public void tearDown() throws Exception {
088    // testAppendClose closes the FileSystem, which will prevent us from closing cleanly here.
089    try {
090      wals.close();
091    } catch (IOException exception) {
092      LOG.warn("Encountered exception while closing wal factory. If you have other errors, this" +
093        " may be the cause. Message: " + exception);
094      LOG.debug("Exception details for failure to close wal factory.", exception);
095    }
096    FileStatus[] entries = fs.listStatus(new Path("/"));
097    for (FileStatus dir : entries) {
098      fs.delete(dir.getPath(), true);
099    }
100  }
101
102  @BeforeClass
103  public static void setUpBeforeClass() throws Exception {
104    TEST_UTIL.startMiniDFSCluster(3);
105    conf = TEST_UTIL.getConfiguration();
106    conf.set(WALFactory.WAL_PROVIDER, MyFSWalProvider.class.getName());
107    conf.set(WALFactory.META_WAL_PROVIDER, MyFSWalProvider.class.getName());
108    cluster = TEST_UTIL.getDFSCluster();
109
110    hbaseDir = TEST_UTIL.createRootDir();
111    hbaseWALDir = TEST_UTIL.createWALRootDir();
112  }
113
114  @AfterClass
115  public static void tearDownAfterClass() throws Exception {
116    TEST_UTIL.shutdownMiniCluster();
117  }
118
119
120  private static MyFSLog myFSLogCreated;
121  private static boolean throwExceptionWhenCloseFSLogClose = false;
122
123  @Test
124  public void testWALClosedIfOpenError() throws IOException {
125
126    throwExceptionWhenCloseFSLogClose = false;
127
128    boolean hasFakeInitException = false;
129    try {
130      wals.getWAL(HRegionInfo.FIRST_META_REGIONINFO);
131    } catch (IOException ex) {
132      hasFakeInitException = ex.getMessage().contains("Fake init exception");
133    }
134    Assert.assertTrue(hasFakeInitException);
135    Assert.assertTrue(myFSLogCreated.closed);
136
137    FileStatus[] fileStatuses = CommonFSUtils.listStatus(fs, myFSLogCreated.walDir);
138    Assert.assertTrue(fileStatuses == null || fileStatuses.length == 0);
139  }
140
141  @Test
142  public void testThrowFailedCloseWalException() throws IOException {
143    throwExceptionWhenCloseFSLogClose = true;
144    boolean failedCloseWalException = false;
145    try {
146      wals.getWAL(HRegionInfo.FIRST_META_REGIONINFO);
147    } catch (FailedCloseWALAfterInitializedErrorException ex) {
148      failedCloseWalException = true;
149    }
150    Assert.assertTrue(failedCloseWalException);
151  }
152
153
154  public static class MyFSWalProvider extends FSHLogProvider {
155
156    @Override
157    protected MyFSLog createWAL() throws IOException {
158      MyFSLog myFSLog = new MyFSLog(CommonFSUtils.getWALFileSystem(conf),
159        CommonFSUtils.getWALRootDir(conf), getWALDirectoryName(factory.getFactoryId()),
160        getWALArchiveDirectoryName(conf, factory.getFactoryId()), conf, listeners, true, logPrefix,
161        META_WAL_PROVIDER_ID.equals(providerId) ? META_WAL_PROVIDER_ID : null);
162
163      myFSLogCreated = myFSLog;
164
165      return myFSLog;
166    }
167  }
168
169  public static class MyFSLog extends FSHLog {
170    public MyFSLog(final FileSystem fs, final Path rootDir, final String logDir,
171      final String archiveDir, final Configuration conf, final List<WALActionsListener> listeners,
172      final boolean failIfWALExists, final String prefix, final String suffix) throws IOException {
173      super(fs, rootDir, logDir, archiveDir, conf, listeners, failIfWALExists, prefix, suffix);
174    }
175
176    @Override
177    public void init() throws IOException {
178      super.init();
179      throw new IOException("Fake init exception");
180    }
181
182    @Override
183    public void close() throws IOException {
184      if (throwExceptionWhenCloseFSLogClose) {
185        throw new IOException("Fake close exception");
186      }
187      super.close();
188    }
189  }
190}