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;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotNull;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.List;
028import java.util.NavigableMap;
029import org.apache.hadoop.hbase.client.ClusterConnection;
030import org.apache.hadoop.hbase.client.HConnectionTestingUtility;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.client.RegionInfoBuilder;
033import org.apache.hadoop.hbase.client.Result;
034import org.apache.hadoop.hbase.client.Table;
035import org.apache.hadoop.hbase.client.coprocessor.Batch;
036import org.apache.hadoop.hbase.ipc.HBaseRpcController;
037import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos;
038import org.apache.hadoop.hbase.testclassification.MiscTests;
039import org.apache.hadoop.hbase.testclassification.SmallTests;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
042import org.junit.After;
043import org.junit.Before;
044import org.junit.ClassRule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047import org.mockito.Mock;
048import org.mockito.Mockito;
049import org.mockito.internal.matchers.Any;
050import org.mockito.invocation.InvocationOnMock;
051import org.mockito.stubbing.Answer;
052import org.slf4j.Logger;
053import org.slf4j.LoggerFactory;
054import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
055import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
056import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos;
057import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanRequest;
058import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
059
060/**
061 * Test MetaTableAccessor but without spinning up a cluster.
062 * We mock regionserver back and forth (we do spin up a zk cluster).
063 */
064@Category({MiscTests.class, SmallTests.class})
065public class TestMetaTableAccessorNoCluster {
066
067  @ClassRule
068  public static final HBaseClassTestRule CLASS_RULE =
069      HBaseClassTestRule.forClass(TestMetaTableAccessorNoCluster.class);
070
071  private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableAccessorNoCluster.class);
072  private static final  HBaseTestingUtility UTIL = new HBaseTestingUtility();
073  private static final Abortable ABORTABLE = new Abortable() {
074    boolean aborted = false;
075    @Override
076    public void abort(String why, Throwable e) {
077      LOG.info(why, e);
078      this.aborted = true;
079      throw new RuntimeException(e);
080    }
081    @Override
082    public boolean isAborted()  {
083      return this.aborted;
084    }
085  };
086
087  @Before
088  public void before() throws Exception {
089    UTIL.startMiniZKCluster();
090  }
091
092  @After
093  public void after() throws IOException {
094    UTIL.shutdownMiniZKCluster();
095  }
096
097  /**
098   * Expect a IOE to come out of multiMutate, even if down in the depths we throw
099   * a RuntimeException. See HBASE-23904
100   */
101  @Test (expected = IOException.class)
102  public void testMultiMutate() throws Throwable {
103    Table table = Mockito.mock(Table.class);
104    Mockito.when(table.coprocessorService(Mockito.any(),
105      Mockito.any(byte [].class), Mockito.any(byte [].class), Mockito.any(Batch.Call.class))).
106      thenThrow(new RuntimeException("FAIL TEST WITH RuntimeException!"));
107    MetaTableAccessor.multiMutate(table, HConstants.LAST_ROW, Collections.emptyList());
108  }
109
110  @Test
111  public void testGetHRegionInfo() throws IOException {
112    assertNull(MetaTableAccessor.getRegionInfo(new Result()));
113
114    List<Cell> kvs = new ArrayList<>();
115    Result r = Result.create(kvs);
116    assertNull(MetaTableAccessor.getRegionInfo(r));
117
118    byte [] f = HConstants.CATALOG_FAMILY;
119    // Make a key value that doesn't have the expected qualifier.
120    kvs.add(new KeyValue(HConstants.EMPTY_BYTE_ARRAY, f,
121      HConstants.SERVER_QUALIFIER, f));
122    r = Result.create(kvs);
123    assertNull(MetaTableAccessor.getRegionInfo(r));
124    // Make a key that does not have a regioninfo value.
125    kvs.add(new KeyValue(HConstants.EMPTY_BYTE_ARRAY, f,
126      HConstants.REGIONINFO_QUALIFIER, f));
127    RegionInfo hri = MetaTableAccessor.getRegionInfo(Result.create(kvs));
128    assertTrue(hri == null);
129    // OK, give it what it expects
130    kvs.clear();
131    kvs.add(new KeyValue(HConstants.EMPTY_BYTE_ARRAY, f, HConstants.REGIONINFO_QUALIFIER,
132      RegionInfo.toByteArray(RegionInfoBuilder.FIRST_META_REGIONINFO)));
133    hri = MetaTableAccessor.getRegionInfo(Result.create(kvs));
134    assertNotNull(hri);
135    assertTrue(RegionInfo.COMPARATOR.compare(hri, RegionInfoBuilder.FIRST_META_REGIONINFO) == 0);
136  }
137
138  /**
139   * Test that MetaTableAccessor will ride over server throwing
140   * "Server not running" IOEs.
141   * @see <a href="https://issues.apache.org/jira/browse/HBASE-3446">HBASE-3446</a>
142   */
143  @Test
144  public void testRideOverServerNotRunning()
145      throws IOException, InterruptedException, ServiceException {
146    // Need a zk watcher.
147    ZKWatcher zkw = new ZKWatcher(UTIL.getConfiguration(),
148      this.getClass().getSimpleName(), ABORTABLE, true);
149    // This is a servername we use in a few places below.
150    ServerName sn = ServerName.valueOf("example.com", 1234, System.currentTimeMillis());
151
152    ClusterConnection connection = null;
153    try {
154      // Mock an ClientProtocol. Our mock implementation will fail a few
155      // times when we go to open a scanner.
156      final ClientProtos.ClientService.BlockingInterface implementation =
157        Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
158      // When scan called throw IOE 'Server not running' a few times
159      // before we return a scanner id.  Whats WEIRD is that these
160      // exceptions do not show in the log because they are caught and only
161      // printed if we FAIL.  We eventually succeed after retry so these don't
162      // show.  We will know if they happened or not because we will ask
163      // mockito at the end of this test to verify that scan was indeed
164      // called the wanted number of times.
165      List<Cell> kvs = new ArrayList<>();
166      final byte [] rowToVerify = Bytes.toBytes("rowToVerify");
167      kvs.add(new KeyValue(rowToVerify,
168        HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
169          RegionInfo.toByteArray(RegionInfoBuilder.FIRST_META_REGIONINFO)));
170      kvs.add(new KeyValue(rowToVerify,
171        HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
172        Bytes.toBytes(sn.getHostAndPort())));
173      kvs.add(new KeyValue(rowToVerify,
174        HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
175        Bytes.toBytes(sn.getStartcode())));
176      final List<CellScannable> cellScannables = new ArrayList<>(1);
177      cellScannables.add(Result.create(kvs));
178      final ScanResponse.Builder builder = ScanResponse.newBuilder();
179      for (CellScannable result : cellScannables) {
180        builder.addCellsPerResult(((Result)result).size());
181      }
182      Mockito.when(implementation.scan((RpcController) Mockito.any(), (ScanRequest) Mockito.any()))
183          .thenThrow(new ServiceException("Server not running (1 of 3)"))
184          .thenThrow(new ServiceException("Server not running (2 of 3)"))
185          .thenThrow(new ServiceException("Server not running (3 of 3)"))
186          .thenAnswer(new Answer<ScanResponse>() {
187            @Override
188            public ScanResponse answer(InvocationOnMock invocation) throws Throwable {
189              ((HBaseRpcController) invocation.getArgument(0)).setCellScanner(CellUtil
190                  .createCellScanner(cellScannables));
191              return builder.setScannerId(1234567890L).setMoreResults(false).build();
192            }
193          });
194      // Associate a spied-upon Connection with UTIL.getConfiguration.  Need
195      // to shove this in here first so it gets picked up all over; e.g. by
196      // HTable.
197      connection = HConnectionTestingUtility.getSpiedConnection(UTIL.getConfiguration());
198
199      // Fix the location lookup so it 'works' though no network.  First
200      // make an 'any location' object.
201      final HRegionLocation anyLocation =
202        new HRegionLocation(RegionInfoBuilder.FIRST_META_REGIONINFO, sn);
203      final RegionLocations rl = new RegionLocations(anyLocation);
204      // Return the RegionLocations object when locateRegion
205      // The ugly format below comes of 'Important gotcha on spying real objects!' from
206      // http://mockito.googlecode.com/svn/branches/1.6/javadoc/org/mockito/Mockito.html
207      Mockito.doReturn(rl).when(connection).
208        locateRegion((TableName)Mockito.any(), (byte[])Mockito.any(),
209              Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyInt());
210
211      // Now shove our HRI implementation into the spied-upon connection.
212      Mockito.doReturn(implementation).
213        when(connection).getClient(Mockito.any());
214
215      // Scan meta for user tables and verify we got back expected answer.
216      NavigableMap<RegionInfo, Result> hris =
217        MetaTableAccessor.getServerUserRegions(connection, sn);
218      assertEquals(1, hris.size());
219      assertTrue(RegionInfo.COMPARATOR.compare(hris.firstEntry().getKey(),
220        RegionInfoBuilder.FIRST_META_REGIONINFO) == 0);
221      assertTrue(Bytes.equals(rowToVerify, hris.firstEntry().getValue().getRow()));
222      // Finally verify that scan was called four times -- three times
223      // with exception and then on 4th attempt we succeed
224      Mockito.verify(implementation, Mockito.times(4)).
225        scan((RpcController)Mockito.any(), (ScanRequest)Mockito.any());
226    } finally {
227      if (connection != null && !connection.isClosed()) {
228        connection.close();
229      }
230      zkw.close();
231    }
232  }
233}