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