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.client; 019 020import static org.junit.Assert.assertTrue; 021import static org.junit.Assert.fail; 022 023import java.io.IOException; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.HBaseClassTestRule; 026import org.apache.hadoop.hbase.HBaseConfiguration; 027import org.apache.hadoop.hbase.HConstants; 028import org.apache.hadoop.hbase.TableName; 029import org.apache.hadoop.hbase.ipc.HBaseRpcController; 030import org.apache.hadoop.hbase.ipc.RpcControllerFactory; 031import org.apache.hadoop.hbase.testclassification.ClientTests; 032import org.apache.hadoop.hbase.testclassification.SmallTests; 033import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 034import org.junit.ClassRule; 035import org.junit.Rule; 036import org.junit.Test; 037import org.junit.experimental.categories.Category; 038import org.junit.rules.TestName; 039import org.mockito.Mockito; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043import org.apache.hbase.thirdparty.com.google.protobuf.RpcController; 044 045import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotDoneResponse; 046import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SnapshotResponse; 047 048/** 049 * Test snapshot logic from the client 050 */ 051@Category({ SmallTests.class, ClientTests.class }) 052public class TestSnapshotFromAdmin { 053 054 @ClassRule 055 public static final HBaseClassTestRule CLASS_RULE = 056 HBaseClassTestRule.forClass(TestSnapshotFromAdmin.class); 057 058 private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotFromAdmin.class); 059 060 @Rule 061 public TestName name = new TestName(); 062 063 /** 064 * Test that the logic for doing 'correct' back-off based on exponential increase and the max-time 065 * passed from the server ensures the correct overall waiting for the snapshot to finish. n 066 */ 067 @Test 068 public void testBackoffLogic() throws Exception { 069 final int pauseTime = 100; 070 final int maxWaitTime = 071 HConstants.RETRY_BACKOFF[HConstants.RETRY_BACKOFF.length - 1] * pauseTime; 072 final int numRetries = HConstants.RETRY_BACKOFF.length; 073 // calculate the wait time, if we just do straight backoff (ignoring the expected time from 074 // master) 075 long ignoreExpectedTime = 0; 076 for (int i = 0; i < HConstants.RETRY_BACKOFF.length; i++) { 077 ignoreExpectedTime += HConstants.RETRY_BACKOFF[i] * pauseTime; 078 } 079 // the correct wait time, capping at the maxTime/tries + fudge room 080 final long time = pauseTime * 3L + ((maxWaitTime / numRetries) * 3) + 300L; 081 assertTrue("Capped snapshot wait time isn't less that the uncapped backoff time " 082 + "- further testing won't prove anything.", time < ignoreExpectedTime); 083 084 // setup the mocks 085 ConnectionImplementation mockConnection = Mockito.mock(ConnectionImplementation.class); 086 Configuration conf = HBaseConfiguration.create(); 087 // setup the conf to match the expected properties 088 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, numRetries); 089 conf.setLong("hbase.client.pause", pauseTime); 090 091 // mock the master admin to our mock 092 MasterKeepAliveConnection mockMaster = Mockito.mock(MasterKeepAliveConnection.class); 093 Mockito.when(mockConnection.getConfiguration()).thenReturn(conf); 094 Mockito.when(mockConnection.getMaster()).thenReturn(mockMaster); 095 // we need a real retrying caller 096 RpcRetryingCallerFactory callerFactory = new RpcRetryingCallerFactory(conf); 097 RpcControllerFactory controllerFactory = Mockito.mock(RpcControllerFactory.class); 098 Mockito.when(controllerFactory.newController()) 099 .thenReturn(Mockito.mock(HBaseRpcController.class)); 100 Mockito.when(mockConnection.getRpcRetryingCallerFactory()).thenReturn(callerFactory); 101 Mockito.when(mockConnection.getRpcControllerFactory()).thenReturn(controllerFactory); 102 // set the max wait time for the snapshot to complete 103 SnapshotResponse response = 104 SnapshotResponse.newBuilder().setExpectedTimeout(maxWaitTime).build(); 105 Mockito.when(mockMaster.snapshot((RpcController) Mockito.any(), Mockito.any())) 106 .thenReturn(response); 107 // setup the response 108 IsSnapshotDoneResponse.Builder builder = IsSnapshotDoneResponse.newBuilder(); 109 builder.setDone(false); 110 // first five times, we return false, last we get success 111 Mockito.when(mockMaster.isSnapshotDone((RpcController) Mockito.any(), Mockito.any())) 112 .thenReturn(builder.build(), builder.build(), builder.build(), builder.build(), 113 builder.build(), builder.setDone(true).build()); 114 115 // setup the admin and run the test 116 Admin admin = new HBaseAdmin(mockConnection); 117 String snapshot = "snapshot"; 118 final TableName table = TableName.valueOf(name.getMethodName()); 119 // get start time 120 long start = EnvironmentEdgeManager.currentTime(); 121 admin.snapshot(snapshot, table); 122 long finish = EnvironmentEdgeManager.currentTime(); 123 long elapsed = (finish - start); 124 assertTrue("Elapsed time:" + elapsed + " is more than expected max:" + time, elapsed <= time); 125 admin.close(); 126 } 127 128 /** 129 * Make sure that we validate the snapshot name and the table name before we pass anything across 130 * the wire 131 * @throws Exception on failure 132 */ 133 @Test 134 public void testValidateSnapshotName() throws Exception { 135 ConnectionImplementation mockConnection = Mockito.mock(ConnectionImplementation.class); 136 Configuration conf = HBaseConfiguration.create(); 137 Mockito.when(mockConnection.getConfiguration()).thenReturn(conf); 138 // we need a real retrying caller 139 RpcRetryingCallerFactory callerFactory = new RpcRetryingCallerFactory(conf); 140 RpcControllerFactory controllerFactory = Mockito.mock(RpcControllerFactory.class); 141 Mockito.when(controllerFactory.newController()) 142 .thenReturn(Mockito.mock(HBaseRpcController.class)); 143 Mockito.when(mockConnection.getRpcRetryingCallerFactory()).thenReturn(callerFactory); 144 Mockito.when(mockConnection.getRpcControllerFactory()).thenReturn(controllerFactory); 145 Admin admin = new HBaseAdmin(mockConnection); 146 // check that invalid snapshot names fail 147 failSnapshotStart(admin, new SnapshotDescription(HConstants.SNAPSHOT_DIR_NAME)); 148 failSnapshotStart(admin, new SnapshotDescription("-snapshot")); 149 failSnapshotStart(admin, new SnapshotDescription("snapshot fails")); 150 failSnapshotStart(admin, new SnapshotDescription("snap$hot")); 151 failSnapshotStart(admin, new SnapshotDescription("snap:hot")); 152 // check the table name also get verified 153 failSnapshotDescriptorCreation("snapshot", ".table"); 154 failSnapshotDescriptorCreation("snapshot", "-table"); 155 failSnapshotDescriptorCreation("snapshot", "table fails"); 156 failSnapshotDescriptorCreation("snapshot", "tab%le"); 157 158 // mock the master connection 159 MasterKeepAliveConnection master = Mockito.mock(MasterKeepAliveConnection.class); 160 Mockito.when(mockConnection.getMaster()).thenReturn(master); 161 SnapshotResponse response = SnapshotResponse.newBuilder().setExpectedTimeout(0).build(); 162 Mockito.when(master.snapshot((RpcController) Mockito.any(), Mockito.any())) 163 .thenReturn(response); 164 IsSnapshotDoneResponse doneResponse = IsSnapshotDoneResponse.newBuilder().setDone(true).build(); 165 Mockito.when(master.isSnapshotDone((RpcController) Mockito.any(), Mockito.any())) 166 .thenReturn(doneResponse); 167 168 // make sure that we can use valid names 169 admin.snapshot(new SnapshotDescription("snapshot", TableName.valueOf(name.getMethodName()))); 170 } 171 172 private void failSnapshotStart(Admin admin, SnapshotDescription snapshot) throws IOException { 173 try { 174 admin.snapshot(snapshot); 175 fail("Snapshot should not have succeed with name:" + snapshot.getName()); 176 } catch (IllegalArgumentException e) { 177 LOG.debug("Correctly failed to start snapshot:" + e.getMessage()); 178 } 179 } 180 181 private void failSnapshotDescriptorCreation(final String snapshotName, final String tableName) { 182 try { 183 new SnapshotDescription(snapshotName, tableName); 184 fail("SnapshotDescription should not have succeed with name:" + snapshotName); 185 } catch (IllegalArgumentException e) { 186 LOG.debug("Correctly failed to create SnapshotDescription:" + e.getMessage()); 187 } 188 } 189}