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