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 java.io.IOException;
021import java.util.ArrayList;
022import java.util.List;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.HBaseClassTestRule;
025import org.apache.hadoop.hbase.HBaseTestingUtil;
026import org.apache.hadoop.hbase.Stoppable;
027import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
028import org.apache.hadoop.hbase.testclassification.MasterTests;
029import org.apache.hadoop.hbase.testclassification.SmallTests;
030import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
031import org.junit.ClassRule;
032import org.junit.Test;
033import org.junit.experimental.categories.Category;
034import org.mockito.Mockito;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
039
040/**
041 * Tests for SnapshotsCleanerChore
042 */
043@Category({ MasterTests.class, SmallTests.class })
044public class TestSnapshotCleanerChore {
045
046  @ClassRule
047  public static final HBaseClassTestRule CLASS_RULE =
048    HBaseClassTestRule.forClass(TestSnapshotCleanerChore.class);
049
050  private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotCleanerChore.class);
051
052  private static final HBaseTestingUtil HBASE_TESTING_UTILITY = new HBaseTestingUtil();
053
054  private SnapshotManager snapshotManager;
055
056  private Configuration getSnapshotCleanerConf() {
057    Configuration conf = HBASE_TESTING_UTILITY.getConfiguration();
058    conf.setInt("hbase.master.cleaner.snapshot.interval", 100);
059    return conf;
060  }
061
062  @Test
063  public void testSnapshotCleanerWithoutAnyCompletedSnapshot() throws IOException {
064    snapshotManager = Mockito.mock(SnapshotManager.class);
065    Stoppable stopper = new StoppableImplementation();
066    Configuration conf = getSnapshotCleanerConf();
067    SnapshotCleanerChore snapshotCleanerChore =
068      new SnapshotCleanerChore(stopper, conf, snapshotManager);
069    try {
070      snapshotCleanerChore.chore();
071    } finally {
072      stopper.stop("Stopping Test Stopper");
073    }
074    Mockito.verify(snapshotManager, Mockito.times(0)).deleteSnapshot(Mockito.any());
075  }
076
077  @Test
078  public void testSnapshotCleanerWithNoTtlExpired() throws IOException {
079    snapshotManager = Mockito.mock(SnapshotManager.class);
080    Stoppable stopper = new StoppableImplementation();
081    Configuration conf = getSnapshotCleanerConf();
082    SnapshotCleanerChore snapshotCleanerChore =
083      new SnapshotCleanerChore(stopper, conf, snapshotManager);
084    List<SnapshotProtos.SnapshotDescription> snapshotDescriptionList = new ArrayList<>();
085    snapshotDescriptionList.add(getSnapshotDescription(-2, "snapshot01", "table01",
086      EnvironmentEdgeManager.currentTime() - 100000));
087    snapshotDescriptionList.add(
088      getSnapshotDescription(10, "snapshot02", "table02", EnvironmentEdgeManager.currentTime()));
089    Mockito.when(snapshotManager.getCompletedSnapshots()).thenReturn(snapshotDescriptionList);
090    try {
091      LOG.info("2 Snapshots are completed but TTL is not expired for any of them");
092      snapshotCleanerChore.chore();
093    } finally {
094      stopper.stop("Stopping Test Stopper");
095    }
096    Mockito.verify(snapshotManager, Mockito.times(0)).deleteSnapshot(Mockito.any());
097  }
098
099  @Test
100  public void testSnapshotCleanerWithSomeTtlExpired() throws IOException {
101    snapshotManager = Mockito.mock(SnapshotManager.class);
102    Stoppable stopper = new StoppableImplementation();
103    Configuration conf = getSnapshotCleanerConf();
104    SnapshotCleanerChore snapshotCleanerChore =
105      new SnapshotCleanerChore(stopper, conf, snapshotManager);
106    List<SnapshotProtos.SnapshotDescription> snapshotDescriptionList = new ArrayList<>();
107    snapshotDescriptionList.add(getSnapshotDescription(10, "snapshot01", "table01", 1));
108    snapshotDescriptionList.add(getSnapshotDescription(5, "snapshot02", "table02", 2));
109    snapshotDescriptionList.add(
110      getSnapshotDescription(30, "snapshot01", "table01", EnvironmentEdgeManager.currentTime()));
111    snapshotDescriptionList.add(
112      getSnapshotDescription(0, "snapshot02", "table02", EnvironmentEdgeManager.currentTime()));
113    snapshotDescriptionList.add(
114      getSnapshotDescription(40, "snapshot03", "table03", EnvironmentEdgeManager.currentTime()));
115    Mockito.when(snapshotManager.getCompletedSnapshots()).thenReturn(snapshotDescriptionList);
116    try {
117      LOG.info("5 Snapshots are completed. TTL is expired for 2 them. Going to delete them");
118      snapshotCleanerChore.chore();
119    } finally {
120      stopper.stop("Stopping Test Stopper");
121    }
122    Mockito.verify(snapshotManager, Mockito.times(2)).deleteSnapshot(Mockito.any());
123  }
124
125  @Test
126  public void testSnapshotCleanerWithReadIOE() throws IOException {
127    snapshotManager = Mockito.mock(SnapshotManager.class);
128    Stoppable stopper = new StoppableImplementation();
129    Configuration conf = new HBaseTestingUtil().getConfiguration();
130    SnapshotCleanerChore snapshotCleanerChore =
131      new SnapshotCleanerChore(stopper, conf, snapshotManager);
132    Mockito.when(snapshotManager.getCompletedSnapshots()).thenThrow(IOException.class);
133    try {
134      LOG.info("While getting completed Snapshots, IOException would occur. Hence, No Snapshot"
135        + " should be deleted");
136      snapshotCleanerChore.chore();
137    } finally {
138      stopper.stop("Stopping Test Stopper");
139    }
140    Mockito.verify(snapshotManager, Mockito.times(0)).deleteSnapshot(Mockito.any());
141  }
142
143  @Test
144  public void testSnapshotChoreWithTtlOutOfRange() throws IOException {
145    snapshotManager = Mockito.mock(SnapshotManager.class);
146    Stoppable stopper = new StoppableImplementation();
147    Configuration conf = getSnapshotCleanerConf();
148    List<SnapshotProtos.SnapshotDescription> snapshotDescriptionList = new ArrayList<>();
149    snapshotDescriptionList.add(getSnapshotDescription(Long.MAX_VALUE, "snapshot01", "table01", 1));
150    snapshotDescriptionList.add(getSnapshotDescription(5, "snapshot02", "table02", 2));
151    Mockito.when(snapshotManager.getCompletedSnapshots()).thenReturn(snapshotDescriptionList);
152    SnapshotCleanerChore snapshotCleanerChore =
153      new SnapshotCleanerChore(stopper, conf, snapshotManager);
154    try {
155      LOG.info("Snapshot Chore is disabled. No cleanup performed for Expired Snapshots");
156      snapshotCleanerChore.chore();
157    } finally {
158      stopper.stop("Stopping Test Stopper");
159    }
160    Mockito.verify(snapshotManager, Mockito.times(1)).getCompletedSnapshots();
161  }
162
163  private SnapshotProtos.SnapshotDescription getSnapshotDescription(final long ttl,
164    final String snapshotName, final String tableName, final long snapshotCreationTime) {
165    SnapshotProtos.SnapshotDescription.Builder snapshotDescriptionBuilder =
166      SnapshotProtos.SnapshotDescription.newBuilder();
167    snapshotDescriptionBuilder.setTtl(ttl);
168    snapshotDescriptionBuilder.setName(snapshotName);
169    snapshotDescriptionBuilder.setTable(tableName);
170    snapshotDescriptionBuilder.setType(SnapshotProtos.SnapshotDescription.Type.FLUSH);
171    snapshotDescriptionBuilder.setCreationTime(snapshotCreationTime);
172    return snapshotDescriptionBuilder.build();
173  }
174
175  /**
176   * Simple helper class that just keeps track of whether or not its stopped.
177   */
178  private static class StoppableImplementation implements Stoppable {
179
180    private volatile boolean stop = false;
181
182    @Override
183    public void stop(String why) {
184      this.stop = true;
185    }
186
187    @Override
188    public boolean isStopped() {
189      return this.stop;
190    }
191
192  }
193
194}