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.fail;
021import static org.mockito.ArgumentMatchers.any;
022import static org.mockito.Mockito.atLeast;
023import static org.mockito.Mockito.doReturn;
024import static org.mockito.Mockito.mock;
025import static org.mockito.Mockito.spy;
026import static org.mockito.Mockito.verify;
027import static org.mockito.Mockito.when;
028
029import com.google.protobuf.ServiceException;
030import java.io.IOException;
031import java.util.ArrayList;
032import org.apache.hadoop.conf.Configuration;
033import org.apache.hadoop.hbase.HBaseClassTestRule;
034import org.apache.hadoop.hbase.HBaseConfiguration;
035import org.apache.hadoop.hbase.HBaseTestingUtility;
036import org.apache.hadoop.hbase.HConstants;
037import org.apache.hadoop.hbase.HTableDescriptor;
038import org.apache.hadoop.hbase.MasterNotRunningException;
039import org.apache.hadoop.hbase.PleaseHoldException;
040import org.apache.hadoop.hbase.TableName;
041import org.apache.hadoop.hbase.ZooKeeperConnectionException;
042import org.apache.hadoop.hbase.ipc.HBaseRpcController;
043import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
044import org.apache.hadoop.hbase.testclassification.ClientTests;
045import org.apache.hadoop.hbase.testclassification.SmallTests;
046import org.junit.ClassRule;
047import org.junit.Ignore;
048import org.junit.Rule;
049import org.junit.Test;
050import org.junit.experimental.categories.Category;
051import org.junit.rules.TestName;
052import org.mockito.Mockito;
053import org.mockito.invocation.InvocationOnMock;
054import org.mockito.stubbing.Answer;
055import org.slf4j.Logger;
056import org.slf4j.LoggerFactory;
057
058@Category({ SmallTests.class, ClientTests.class })
059public class TestHBaseAdminNoCluster {
060
061  @ClassRule
062  public static final HBaseClassTestRule CLASS_RULE =
063    HBaseClassTestRule.forClass(TestHBaseAdminNoCluster.class);
064
065  private static final Logger LOG = LoggerFactory.getLogger(TestHBaseAdminNoCluster.class);
066
067  @Rule
068  public TestName name = new TestName();
069
070  /**
071   * Verify that PleaseHoldException gets retried. HBASE-8764
072   */
073  // TODO: Clean up, with Procedure V2 and nonce to prevent the same procedure to call mulitple
074  // time, this test is invalid anymore. Just keep the test around for some time before
075  // fully removing it.
076  @Ignore
077  @Test
078  public void testMasterMonitorCallableRetries()
079    throws MasterNotRunningException, ZooKeeperConnectionException, IOException,
080    org.apache.hbase.thirdparty.com.google.protobuf.ServiceException {
081    Configuration configuration = HBaseConfiguration.create();
082    // Set the pause and retry count way down.
083    configuration.setLong(HConstants.HBASE_CLIENT_PAUSE, 1);
084    final int count = 10;
085    configuration.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, count);
086    // Get mocked connection. Getting the connection will register it so when HBaseAdmin is
087    // constructed with same configuration, it will find this mocked connection.
088    ClusterConnection connection = HConnectionTestingUtility.getMockedConnection(configuration);
089    // Mock so we get back the master interface. Make it so when createTable is called, we throw
090    // the PleaseHoldException.
091    MasterKeepAliveConnection masterAdmin = mock(MasterKeepAliveConnection.class);
092    when(masterAdmin.createTable(any(), any()))
093      .thenThrow(new ServiceException("Test fail").initCause(new PleaseHoldException("test")));
094    when(connection.getMaster()).thenReturn(masterAdmin);
095    Admin admin = new HBaseAdmin(connection);
096    try {
097      HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
098      // Pass any old htable descriptor; not important
099      try {
100        admin.createTable(htd, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE);
101        fail();
102      } catch (RetriesExhaustedException e) {
103        LOG.info("Expected fail", e);
104      }
105      // Assert we were called 'count' times.
106      verify(masterAdmin, atLeast(count)).createTable(any(), any());
107    } finally {
108      admin.close();
109      if (connection != null) connection.close();
110    }
111  }
112
113  @Test
114  public void testMasterOperationsRetries() throws Exception {
115
116    // Admin.listTables()
117    testMasterOperationIsRetried(new MethodCaller() {
118      @Override
119      public void call(Admin admin) throws Exception {
120        admin.listTables();
121      }
122
123      @Override
124      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
125        Mockito.verify(masterAdmin, atLeast(count)).getTableDescriptors(any(), any());
126      }
127    });
128
129    // Admin.listTableNames()
130    testMasterOperationIsRetried(new MethodCaller() {
131      @Override
132      public void call(Admin admin) throws Exception {
133        admin.listTableNames();
134      }
135
136      @Override
137      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
138        Mockito.verify(masterAdmin, atLeast(count)).getTableNames(any(), any());
139      }
140    });
141
142    // Admin.getTableDescriptor()
143    testMasterOperationIsRetried(new MethodCaller() {
144      @Override
145      public void call(Admin admin) throws Exception {
146        admin.getTableDescriptor(TableName.valueOf(name.getMethodName()));
147      }
148
149      @Override
150      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
151        Mockito.verify(masterAdmin, atLeast(count)).getTableDescriptors(any(), any());
152      }
153    });
154
155    // Admin.getTableDescriptorsByTableName()
156    testMasterOperationIsRetried(new MethodCaller() {
157      @Override
158      public void call(Admin admin) throws Exception {
159        admin.getTableDescriptorsByTableName(new ArrayList<>());
160      }
161
162      @Override
163      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
164        Mockito.verify(masterAdmin, atLeast(count)).getTableDescriptors(any(), any());
165      }
166    });
167
168    // Admin.move()
169    testMasterOperationIsRetried(new MethodCaller() {
170      @Override
171      public void call(Admin admin) throws Exception {
172        admin.move(new byte[0]);
173      }
174
175      @Override
176      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
177        Mockito.verify(masterAdmin, atLeast(count)).moveRegion(any(), any());
178      }
179    });
180
181    // Admin.offline()
182    testMasterOperationIsRetried(new MethodCaller() {
183      @Override
184      public void call(Admin admin) throws Exception {
185        admin.offline(new byte[0]);
186      }
187
188      @Override
189      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
190        Mockito.verify(masterAdmin, atLeast(count)).offlineRegion(any(), any());
191      }
192    });
193
194    // Admin.setBalancerRunning()
195    testMasterOperationIsRetried(new MethodCaller() {
196      @Override
197      public void call(Admin admin) throws Exception {
198        admin.setBalancerRunning(true, true);
199      }
200
201      @Override
202      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
203        Mockito.verify(masterAdmin, atLeast(count)).setBalancerRunning(any(), any());
204      }
205    });
206
207    // Admin.balancer()
208    testMasterOperationIsRetried(new MethodCaller() {
209      @Override
210      public void call(Admin admin) throws Exception {
211        admin.balancer();
212      }
213
214      @Override
215      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
216        Mockito.verify(masterAdmin, atLeast(count)).balance(any(), any());
217      }
218    });
219
220    // Admin.enabledCatalogJanitor()
221    testMasterOperationIsRetried(new MethodCaller() {
222      @Override
223      public void call(Admin admin) throws Exception {
224        admin.enableCatalogJanitor(true);
225      }
226
227      @Override
228      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
229        Mockito.verify(masterAdmin, atLeast(count)).enableCatalogJanitor(any(), any());
230      }
231    });
232
233    // Admin.runCatalogScan()
234    testMasterOperationIsRetried(new MethodCaller() {
235      @Override
236      public void call(Admin admin) throws Exception {
237        admin.runCatalogScan();
238      }
239
240      @Override
241      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
242        Mockito.verify(masterAdmin, atLeast(count)).runCatalogScan(any(), any());
243      }
244    });
245
246    // Admin.isCatalogJanitorEnabled()
247    testMasterOperationIsRetried(new MethodCaller() {
248      @Override
249      public void call(Admin admin) throws Exception {
250        admin.isCatalogJanitorEnabled();
251      }
252
253      @Override
254      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
255        Mockito.verify(masterAdmin, atLeast(count)).isCatalogJanitorEnabled(any(), any());
256      }
257    });
258  }
259
260  private static interface MethodCaller {
261    void call(Admin admin) throws Exception;
262
263    void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception;
264  }
265
266  private void testMasterOperationIsRetried(MethodCaller caller) throws Exception {
267    Configuration configuration = HBaseConfiguration.create();
268    // Set the pause and retry count way down.
269    configuration.setLong(HConstants.HBASE_CLIENT_PAUSE, 1);
270    final int count = 10;
271    configuration.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, count);
272
273    ClusterConnection connection = mock(ClusterConnection.class);
274    when(connection.getConfiguration()).thenReturn(configuration);
275    ConnectionConfiguration connectionConfig = new ConnectionConfiguration(configuration);
276    when(connection.getConnectionConfiguration()).thenReturn(connectionConfig);
277    MasterKeepAliveConnection masterAdmin = mock(MasterKeepAliveConnection.class, new Answer() {
278      @Override
279      public Object answer(InvocationOnMock invocation) throws Throwable {
280        if (invocation.getMethod().getName().equals("close")) {
281          return null;
282        }
283        throw new MasterNotRunningException(); // all methods will throw an exception
284      }
285    });
286    when(connection.getMaster()).thenReturn(masterAdmin);
287    RpcControllerFactory rpcControllerFactory = mock(RpcControllerFactory.class);
288    when(connection.getRpcControllerFactory()).thenReturn(rpcControllerFactory);
289    when(rpcControllerFactory.newController()).thenReturn(mock(HBaseRpcController.class));
290
291    // we need a real retrying caller
292    RpcRetryingCallerFactory callerFactory =
293      new RpcRetryingCallerFactory(configuration, connectionConfig);
294    when(connection.getRpcRetryingCallerFactory()).thenReturn(callerFactory);
295
296    Admin admin = null;
297    try {
298      admin = spy(new HBaseAdmin(connection));
299      // mock the call to getRegion since in the absence of a cluster (which means the meta
300      // is not assigned), getRegion can't function
301      doReturn(null).when(((HBaseAdmin) admin)).getRegion(any());
302      try {
303        caller.call(admin); // invoke the HBaseAdmin method
304        fail();
305      } catch (RetriesExhaustedException e) {
306        LOG.info("Expected fail", e);
307      }
308      // Assert we were called 'count' times.
309      caller.verify(masterAdmin, count);
310    } finally {
311      if (admin != null) {
312        admin.close();
313      }
314    }
315  }
316}