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 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
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;
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.fs.FileStatus;
024import org.apache.hadoop.fs.FileSystem;
025import org.apache.hadoop.fs.Path;
026import org.apache.hadoop.hbase.FailedCloseWALAfterInitializedErrorException;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.HRegionInfo;
030import org.apache.hadoop.hbase.ServerName;
031import org.apache.hadoop.hbase.testclassification.MediumTests;
032import org.apache.hadoop.hbase.testclassification.RegionServerTests;
033import org.apache.hadoop.hbase.util.CommonFSUtils;
034import org.apache.hadoop.hbase.wal.FSHLogProvider;
035import org.apache.hadoop.hbase.wal.WALFactory;
036import org.apache.hadoop.hdfs.MiniDFSCluster;
037import org.junit.After;
038import org.junit.AfterClass;
039import org.junit.Assert;
040import org.junit.Before;
041import org.junit.BeforeClass;
042import org.junit.ClassRule;
043import org.junit.Rule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046import org.junit.rules.TestName;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050/**
051 * Test WAL Init ERROR
052 */
053@Category({ RegionServerTests.class, MediumTests.class })
054public class TestWALOpenError {
055
056  @ClassRule
057  public static final HBaseClassTestRule CLASS_RULE =
058    HBaseClassTestRule.forClass(TestWALOpenError.class);
059
060  private static final Logger LOG = LoggerFactory.getLogger(TestWALOpenError.class);
061
062  protected static Configuration conf;
063  private static MiniDFSCluster cluster;
064  protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
065  protected static Path hbaseDir;
066  protected static Path hbaseWALDir;
067
068  protected FileSystem fs;
069  protected Path dir;
070  protected WALFactory wals;
071  private ServerName currentServername;
072
073  @Rule
074  public final TestName currentTest = new TestName();
075
076  @Before
077  public void setUp() throws Exception {
078    fs = cluster.getFileSystem();
079    dir = new Path(hbaseDir, currentTest.getMethodName());
080    this.currentServername = ServerName.valueOf(currentTest.getMethodName(), 16010, 1);
081    wals = new WALFactory(conf, this.currentServername.toString());
082  }
083
084  @After
085  public void tearDown() throws Exception {
086    // testAppendClose closes the FileSystem, which will prevent us from closing cleanly here.
087    try {
088      wals.close();
089    } catch (IOException exception) {
090      LOG.warn("Encountered exception while closing wal factory. If you have other errors, this"
091        + " may be the cause. Message: " + exception);
092      LOG.debug("Exception details for failure to close wal factory.", exception);
093    }
094    FileStatus[] entries = fs.listStatus(new Path("/"));
095    for (FileStatus dir : entries) {
096      fs.delete(dir.getPath(), true);
097    }
098  }
099
100  @BeforeClass
101  public static void setUpBeforeClass() throws Exception {
102    TEST_UTIL.startMiniDFSCluster(3);
103    conf = TEST_UTIL.getConfiguration();
104    conf.set(WALFactory.WAL_PROVIDER, MyFSWalProvider.class.getName());
105    conf.set(WALFactory.META_WAL_PROVIDER, MyFSWalProvider.class.getName());
106    cluster = TEST_UTIL.getDFSCluster();
107
108    hbaseDir = TEST_UTIL.createRootDir();
109    hbaseWALDir = TEST_UTIL.createWALRootDir();
110  }
111
112  @AfterClass
113  public static void tearDownAfterClass() throws Exception {
114    TEST_UTIL.shutdownMiniCluster();
115  }
116
117  private static MyFSLog myFSLogCreated;
118  private static boolean throwExceptionWhenCloseFSLogClose = false;
119
120  @Test
121  public void testWALClosedIfOpenError() throws IOException {
122
123    throwExceptionWhenCloseFSLogClose = false;
124
125    boolean hasFakeInitException = false;
126    try {
127      wals.getWAL(HRegionInfo.FIRST_META_REGIONINFO);
128    } catch (IOException ex) {
129      hasFakeInitException = ex.getMessage().contains("Fake init exception");
130    }
131    Assert.assertTrue(hasFakeInitException);
132    Assert.assertTrue(myFSLogCreated.closed);
133
134    FileStatus[] fileStatuses = CommonFSUtils.listStatus(fs, myFSLogCreated.walDir);
135    Assert.assertTrue(fileStatuses == null || fileStatuses.length == 0);
136  }
137
138  @Test
139  public void testThrowFailedCloseWalException() throws IOException {
140    throwExceptionWhenCloseFSLogClose = true;
141    boolean failedCloseWalException = false;
142    try {
143      wals.getWAL(HRegionInfo.FIRST_META_REGIONINFO);
144    } catch (FailedCloseWALAfterInitializedErrorException ex) {
145      failedCloseWalException = true;
146    }
147    Assert.assertTrue(failedCloseWalException);
148  }
149
150  public static class MyFSWalProvider extends FSHLogProvider {
151
152    @Override
153    protected MyFSLog createWAL() throws IOException {
154      MyFSLog myFSLog = new MyFSLog(CommonFSUtils.getWALFileSystem(conf),
155        CommonFSUtils.getWALRootDir(conf), getWALDirectoryName(factory.getFactoryId()),
156        getWALArchiveDirectoryName(conf, factory.getFactoryId()), conf, listeners, true, logPrefix,
157        META_WAL_PROVIDER_ID.equals(providerId) ? META_WAL_PROVIDER_ID : null);
158
159      myFSLogCreated = myFSLog;
160
161      return myFSLog;
162    }
163  }
164
165  public static class MyFSLog extends FSHLog {
166    public MyFSLog(final FileSystem fs, final Path rootDir, final String logDir,
167      final String archiveDir, final Configuration conf, final List<WALActionsListener> listeners,
168      final boolean failIfWALExists, final String prefix, final String suffix) throws IOException {
169      super(fs, rootDir, logDir, archiveDir, conf, listeners, failIfWALExists, prefix, suffix);
170    }
171
172    @Override
173    public void init() throws IOException {
174      super.init();
175      throw new IOException("Fake init exception");
176    }
177
178    @Override
179    public void close() throws IOException {
180      if (throwExceptionWhenCloseFSLogClose) {
181        throw new IOException("Fake close exception");
182      }
183      super.close();
184    }
185  }
186}