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.master.cleaner;
019
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import java.io.IOException;
025import java.io.UncheckedIOException;
026import java.util.ArrayList;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Map;
030import org.apache.hadoop.conf.Configuration;
031import org.apache.hadoop.fs.FileStatus;
032import org.apache.hadoop.fs.FileSystem;
033import org.apache.hadoop.fs.Path;
034import org.apache.hadoop.hbase.HBaseClassTestRule;
035import org.apache.hadoop.hbase.HBaseTestingUtil;
036import org.apache.hadoop.hbase.HConstants;
037import org.apache.hadoop.hbase.Server;
038import org.apache.hadoop.hbase.TableName;
039import org.apache.hadoop.hbase.client.Connection;
040import org.apache.hadoop.hbase.client.TableDescriptor;
041import org.apache.hadoop.hbase.keymeta.KeyManagementService;
042import org.apache.hadoop.hbase.master.HMaster;
043import org.apache.hadoop.hbase.replication.ReplicationException;
044import org.apache.hadoop.hbase.replication.ReplicationFactory;
045import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
046import org.apache.hadoop.hbase.replication.ReplicationPeers;
047import org.apache.hadoop.hbase.replication.ReplicationQueueStorage;
048import org.apache.hadoop.hbase.replication.ReplicationStorageFactory;
049import org.apache.hadoop.hbase.replication.SyncReplicationState;
050import org.apache.hadoop.hbase.replication.master.ReplicationHFileCleaner;
051import org.apache.hadoop.hbase.testclassification.MasterTests;
052import org.apache.hadoop.hbase.testclassification.SmallTests;
053import org.apache.hadoop.hbase.util.MockServer;
054import org.apache.hadoop.hbase.util.Pair;
055import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
056import org.junit.After;
057import org.junit.AfterClass;
058import org.junit.Before;
059import org.junit.BeforeClass;
060import org.junit.ClassRule;
061import org.junit.Test;
062import org.junit.experimental.categories.Category;
063import org.slf4j.Logger;
064import org.slf4j.LoggerFactory;
065
066import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap;
067
068@Category({ MasterTests.class, SmallTests.class })
069public class TestReplicationHFileCleaner {
070
071  @ClassRule
072  public static final HBaseClassTestRule CLASS_RULE =
073    HBaseClassTestRule.forClass(TestReplicationHFileCleaner.class);
074
075  private static final Logger LOG = LoggerFactory.getLogger(TestReplicationHFileCleaner.class);
076  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
077  private static Server server;
078  private static final TableName tableName = TableName.valueOf("test_cleaner");
079  private static ReplicationQueueStorage rq;
080  private static ReplicationPeers rp;
081  private static final String peerId = "TestReplicationHFileCleaner";
082  private static Configuration conf = TEST_UTIL.getConfiguration();
083  private static FileSystem fs = null;
084  private static Map<String, Object> params;
085  private Path root;
086
087  @BeforeClass
088  public static void setUpBeforeClass() throws Exception {
089    TEST_UTIL.startMiniCluster();
090    server = new DummyServer();
091    params = ImmutableMap.of(HMaster.MASTER, server);
092    conf.setBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, true);
093    HMaster.decorateMasterConfiguration(conf);
094    TableDescriptor td = ReplicationStorageFactory.createReplicationQueueTableDescriptor(tableName);
095    TEST_UTIL.getAdmin().createTable(td);
096    conf.set(ReplicationStorageFactory.REPLICATION_QUEUE_TABLE_NAME, tableName.getNameAsString());
097    rp =
098      ReplicationFactory.getReplicationPeers(server.getFileSystem(), server.getZooKeeper(), conf);
099    rp.init();
100    rq = ReplicationStorageFactory.getReplicationQueueStorage(server.getConnection(), conf);
101    fs = FileSystem.get(conf);
102  }
103
104  @AfterClass
105  public static void tearDownAfterClass() throws Exception {
106    TEST_UTIL.shutdownMiniCluster();
107  }
108
109  @Before
110  public void setup() throws ReplicationException, IOException {
111    root = TEST_UTIL.getDataTestDirOnTestFS();
112    rp.getPeerStorage().addPeer(peerId,
113      ReplicationPeerConfig.newBuilder().setClusterKey(TEST_UTIL.getRpcConnnectionURI()).build(),
114      true, SyncReplicationState.NONE);
115  }
116
117  @After
118  public void cleanup() throws ReplicationException {
119    try {
120      fs.delete(root, true);
121    } catch (IOException e) {
122      LOG.warn("Failed to delete files recursively from path " + root);
123    }
124    // Remove all HFileRefs (if any)
125    rq.removeHFileRefs(peerId, rq.getReplicableHFiles(peerId));
126    rp.getPeerStorage().removePeer(peerId);
127  }
128
129  private ReplicationHFileCleaner createCleaner() {
130    ReplicationHFileCleaner cleaner = new ReplicationHFileCleaner();
131    cleaner.setConf(conf);
132    cleaner.init(params);
133    return cleaner;
134  }
135
136  @Test
137  public void testIsFileDeletable() throws IOException, ReplicationException {
138    // 1. Create a file
139    Path file = new Path(root, "testIsFileDeletableWithNoHFileRefs");
140    fs.createNewFile(file);
141    // 2. Assert file is successfully created
142    assertTrue("Test file not created!", fs.exists(file));
143    ReplicationHFileCleaner cleaner = createCleaner();
144    // 3. Assert that file as is should be deletable
145    assertTrue("Cleaner should allow to delete this file as there is no hfile reference node "
146      + "for it in the queue.", cleaner.isFileDeletable(fs.getFileStatus(file)));
147
148    List<Pair<Path, Path>> files = new ArrayList<>(1);
149    files.add(new Pair<>(null, file));
150    // 4. Add the file to hfile-refs queue
151    rq.addHFileRefs(peerId, files);
152    // 5. Assert file should not be deletable
153    assertFalse("Cleaner should not allow to delete this file as there is a hfile reference node "
154      + "for it in the queue.", cleaner.isFileDeletable(fs.getFileStatus(file)));
155  }
156
157  @Test
158  public void testGetDeletableFiles() throws Exception {
159    // 1. Create two files and assert that they do not exist
160    Path notDeletablefile = new Path(root, "testGetDeletableFiles_1");
161    fs.createNewFile(notDeletablefile);
162    assertTrue("Test file not created!", fs.exists(notDeletablefile));
163    Path deletablefile = new Path(root, "testGetDeletableFiles_2");
164    fs.createNewFile(deletablefile);
165    assertTrue("Test file not created!", fs.exists(deletablefile));
166
167    List<FileStatus> files = new ArrayList<>(2);
168    FileStatus f = new FileStatus();
169    f.setPath(deletablefile);
170    files.add(f);
171    f = new FileStatus();
172    f.setPath(notDeletablefile);
173    files.add(f);
174
175    List<Pair<Path, Path>> hfiles = new ArrayList<>(1);
176    hfiles.add(new Pair<>(null, notDeletablefile));
177    // 2. Add one file to hfile-refs queue
178    rq.addHFileRefs(peerId, hfiles);
179
180    ReplicationHFileCleaner cleaner = createCleaner();
181    Iterator<FileStatus> deletableFilesIterator = cleaner.getDeletableFiles(files).iterator();
182    int i = 0;
183    while (deletableFilesIterator.hasNext() && i < 2) {
184      i++;
185    }
186    // 5. Assert one file should not be deletable and it is present in the list returned
187    if (i > 2) {
188      fail("File " + notDeletablefile
189        + " should not be deletable as its hfile reference node is not added.");
190    }
191    assertTrue(deletableFilesIterator.next().getPath().equals(deletablefile));
192  }
193
194  static class DummyServer extends MockServer {
195
196    @Override
197    public Configuration getConfiguration() {
198      return TEST_UTIL.getConfiguration();
199    }
200
201    @Override
202    public ZKWatcher getZooKeeper() {
203      try {
204        return TEST_UTIL.getZooKeeperWatcher();
205      } catch (IOException e) {
206        throw new UncheckedIOException(e);
207      }
208    }
209
210    @Override
211    public Connection getConnection() {
212      try {
213        return TEST_UTIL.getConnection();
214      } catch (IOException e) {
215        throw new UncheckedIOException(e);
216      }
217    }
218
219    @Override
220    public FileSystem getFileSystem() {
221      try {
222        return TEST_UTIL.getTestFileSystem();
223      } catch (IOException e) {
224        throw new UncheckedIOException(e);
225      }
226    }
227
228    @Override
229    public KeyManagementService getKeyManagementService() {
230      return null;
231    }
232  }
233}