1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.client;
19  
20  import static org.junit.Assert.assertTrue;
21  import static org.junit.Assert.fail;
22  
23  import java.io.IOException;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.conf.Configuration;
28  import org.apache.hadoop.hbase.HBaseConfiguration;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.SmallTests;
31  import org.apache.hadoop.hbase.ipc.HMasterInterface;
32  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
33  import org.apache.hadoop.hbase.snapshot.HSnapshotDescription;
34  import org.junit.Test;
35  import org.junit.experimental.categories.Category;
36  import org.mockito.Mockito;
37  
38  import com.google.protobuf.RpcController;
39  
40  /**
41   * Test snapshot logic from the client
42   */
43  @Category(SmallTests.class)
44  public class TestSnapshotFromAdmin {
45  
46    private static final Log LOG = LogFactory.getLog(TestSnapshotFromAdmin.class);
47  
48    /**
49     * Test that the logic for doing 'correct' back-off based on exponential increase and the max-time
50     * passed from the server ensures the correct overall waiting for the snapshot to finish.
51     * @throws Exception
52     */
53    @Test(timeout = 10000)
54    public void testBackoffLogic() throws Exception {
55      final int maxWaitTime = 7500;
56      final int numRetries = 10;
57      final int pauseTime = 500;
58      // calculate the wait time, if we just do straight backoff (ignoring the expected time from
59      // master)
60      long ignoreExpectedTime = 0;
61      for (int i = 0; i < 6; i++) {
62        ignoreExpectedTime += HConstants.RETRY_BACKOFF[i] * pauseTime;
63      }
64      // the correct wait time, capping at the maxTime/tries + fudge room
65      final long time = pauseTime * 3 + ((maxWaitTime / numRetries) * 3) + 300;
66      assertTrue("Capped snapshot wait time isn't less that the uncapped backoff time "
67          + "- further testing won't prove anything.", time < ignoreExpectedTime);
68  
69      // setup the mocks
70      HConnectionManager.HConnectionImplementation mockConnection = Mockito
71          .mock(HConnectionManager.HConnectionImplementation.class);
72      Configuration conf = HBaseConfiguration.create();
73      // setup the conf to match the expected properties
74      conf.setInt("hbase.client.retries.number", numRetries);
75      conf.setLong("hbase.client.pause", pauseTime);
76      // mock the master admin to our mock
77      HMasterInterface mockMaster = Mockito.mock(HMasterInterface.class);
78      Mockito.when(mockConnection.getConfiguration()).thenReturn(conf);
79      Mockito.when(mockConnection.getMaster()).thenReturn(mockMaster);
80      // set the max wait time for the snapshot to complete
81      Mockito
82          .when(
83            mockMaster.snapshot(
84              Mockito.any(HSnapshotDescription.class))).thenReturn((long)maxWaitTime);
85  
86      // first five times, we return false, last we get success
87      Mockito.when(
88        mockMaster.isSnapshotDone(
89          Mockito.any(HSnapshotDescription.class))).thenReturn(false, false,
90        false, false, false, true);
91  
92      // setup the admin and run the test
93      HBaseAdmin admin = new HBaseAdmin(mockConnection);
94      String snapshot = "snapshot";
95      String table = "table";
96      // get start time
97      long start = System.currentTimeMillis();
98      admin.snapshot(snapshot, table);
99      long finish = System.currentTimeMillis();
100     long elapsed = (finish - start);
101     assertTrue("Elapsed time:" + elapsed + " is more than expected max:" + time, elapsed <= time);
102     admin.close();
103   }
104 
105   /**
106    * Make sure that we validate the snapshot name and the table name before we pass anything across
107    * the wire
108    * @throws Exception on failure
109    */
110   @Test
111   public void testValidateSnapshotName() throws Exception {
112     HConnectionManager.HConnectionImplementation mockConnection = Mockito
113         .mock(HConnectionManager.HConnectionImplementation.class);
114     Configuration conf = HBaseConfiguration.create();
115     Mockito.when(mockConnection.getConfiguration()).thenReturn(conf);
116     HBaseAdmin admin = new HBaseAdmin(mockConnection);
117     SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
118     // check that invalid snapshot names fail
119     failSnapshotStart(admin, builder.setName(".snapshot").build());
120     failSnapshotStart(admin, builder.setName("-snapshot").build());
121     failSnapshotStart(admin, builder.setName("snapshot fails").build());
122     failSnapshotStart(admin, builder.setName("snap$hot").build());
123     // check the table name also get verified
124     failSnapshotStart(admin, builder.setName("snapshot").setTable(".table").build());
125     failSnapshotStart(admin, builder.setName("snapshot").setTable("-table").build());
126     failSnapshotStart(admin, builder.setName("snapshot").setTable("table fails").build());
127     failSnapshotStart(admin, builder.setName("snapshot").setTable("tab%le").build());
128 
129     // mock the master connection
130     HMasterInterface master = Mockito.mock(HMasterInterface.class);
131     Mockito.when(mockConnection.getMaster()).thenReturn(master);
132 
133     Mockito.when(
134       master.snapshot(Mockito.any(HSnapshotDescription.class))).thenReturn((long)0);
135     Mockito.when(
136       master.isSnapshotDone(
137         Mockito.any(HSnapshotDescription.class))).thenReturn(true);
138 
139       // make sure that we can use valid names
140     admin.snapshot(builder.setName("snapshot").setTable("table").build());
141   }
142 
143   private void failSnapshotStart(HBaseAdmin admin, SnapshotDescription snapshot) throws IOException {
144     try {
145       admin.snapshot(snapshot);
146       fail("Snapshot should not have succeed with name:" + snapshot.getName());
147     } catch (IllegalArgumentException e) {
148       LOG.debug("Correctly failed to start snapshot:" + e.getMessage());
149     }
150   }
151 }