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.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertNull;
022import static org.junit.jupiter.api.Assertions.assertThrows;
023import static org.junit.jupiter.api.Assertions.assertTrue;
024
025import java.io.IOException;
026import org.apache.hadoop.hbase.master.RegionState;
027import org.apache.hadoop.hbase.testclassification.MediumTests;
028import org.apache.hadoop.hbase.testclassification.MiscTests;
029import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
030import org.apache.hadoop.hbase.util.Threads;
031import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
032import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
033import org.apache.zookeeper.KeeperException;
034import org.junit.jupiter.api.AfterAll;
035import org.junit.jupiter.api.AfterEach;
036import org.junit.jupiter.api.BeforeAll;
037import org.junit.jupiter.api.BeforeEach;
038import org.junit.jupiter.api.Tag;
039import org.junit.jupiter.api.Test;
040import org.mockito.Mockito;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
045import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
046
047import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos;
048import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.GetRequest;
049import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.GetResponse;
050
051/**
052 * Test {@link org.apache.hadoop.hbase.zookeeper.MetaTableLocator}
053 */
054@Tag(MiscTests.TAG)
055@Tag(MediumTests.TAG)
056public class TestMetaTableLocator {
057
058  private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableLocator.class);
059  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
060  private static final ServerName SN =
061    ServerName.valueOf("example.org", 1234, EnvironmentEdgeManager.currentTime());
062  private ZKWatcher watcher;
063  private Abortable abortable;
064
065  @BeforeAll
066  public static void beforeClass() throws Exception {
067    // Set this down so tests run quicker
068    UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 3);
069    UTIL.startMiniZKCluster();
070  }
071
072  @AfterAll
073  public static void afterClass() throws IOException {
074    UTIL.getZkCluster().shutdown();
075  }
076
077  @BeforeEach
078  public void before() throws IOException {
079    this.abortable = new Abortable() {
080      @Override
081      public void abort(String why, Throwable e) {
082        LOG.info(why, e);
083      }
084
085      @Override
086      public boolean isAborted() {
087        return false;
088      }
089    };
090    this.watcher =
091      new ZKWatcher(UTIL.getConfiguration(), this.getClass().getSimpleName(), this.abortable, true);
092  }
093
094  @AfterEach
095  public void after() {
096    try {
097      // Clean out meta location or later tests will be confused... they presume
098      // start fresh in zk.
099      MetaTableLocator.deleteMetaLocation(this.watcher);
100    } catch (KeeperException e) {
101      LOG.warn("Unable to delete hbase:meta location", e);
102    }
103
104    this.watcher.close();
105  }
106
107  /**
108   * Test normal operations
109   */
110  @Test
111  public void testMetaLookup()
112    throws IOException, InterruptedException, ServiceException, KeeperException {
113    final ClientProtos.ClientService.BlockingInterface client =
114      Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
115
116    Mockito.when(client.get((RpcController) Mockito.any(), (GetRequest) Mockito.any()))
117      .thenReturn(GetResponse.newBuilder().build());
118
119    assertNull(MetaTableLocator.getMetaRegionLocation(this.watcher));
120    for (RegionState.State state : RegionState.State.values()) {
121      if (state.equals(RegionState.State.OPEN)) {
122        continue;
123      }
124      MetaTableLocator.setMetaLocation(this.watcher, SN, state);
125      assertNull(MetaTableLocator.getMetaRegionLocation(this.watcher));
126      assertEquals(state, MetaTableLocator.getMetaRegionState(this.watcher).getState());
127    }
128    MetaTableLocator.setMetaLocation(this.watcher, SN, RegionState.State.OPEN);
129    assertEquals(SN, MetaTableLocator.getMetaRegionLocation(this.watcher));
130    assertEquals(RegionState.State.OPEN,
131      MetaTableLocator.getMetaRegionState(this.watcher).getState());
132
133    MetaTableLocator.deleteMetaLocation(this.watcher);
134    assertNull(MetaTableLocator.getMetaRegionState(this.watcher).getServerName());
135    assertEquals(RegionState.State.OFFLINE,
136      MetaTableLocator.getMetaRegionState(this.watcher).getState());
137    assertNull(MetaTableLocator.getMetaRegionLocation(this.watcher));
138  }
139
140  @Test
141  public void testTimeoutWaitForMeta() throws IOException, InterruptedException {
142    assertThrows(NotAllMetaRegionsOnlineException.class, () -> {
143      MetaTableLocator.waitMetaRegionLocation(watcher, 100);
144    });
145  }
146
147  /**
148   * Test waiting on meat w/ no timeout specified.
149   */
150  @Test
151  public void testNoTimeoutWaitForMeta() throws IOException, InterruptedException, KeeperException {
152    ServerName hsa = MetaTableLocator.getMetaRegionLocation(watcher);
153    assertNull(hsa);
154
155    // Now test waiting on meta location getting set.
156    Thread t = new WaitOnMetaThread();
157    startWaitAliveThenWaitItLives(t, 1);
158    // Set a meta location.
159    MetaTableLocator.setMetaLocation(this.watcher, SN, RegionState.State.OPEN);
160    hsa = SN;
161    // Join the thread... should exit shortly.
162    t.join();
163    // Now meta is available.
164    assertTrue(MetaTableLocator.getMetaRegionLocation(watcher).equals(hsa));
165  }
166
167  private void startWaitAliveThenWaitItLives(final Thread t, final int ms) {
168    t.start();
169    UTIL.waitFor(2000, t::isAlive);
170    // Wait one second.
171    Threads.sleep(ms);
172    assertTrue(t.isAlive(), "Assert " + t.getName() + " still waiting");
173  }
174
175  /**
176   * Wait on META.
177   */
178  class WaitOnMetaThread extends Thread {
179
180    WaitOnMetaThread() {
181      super("WaitOnMeta");
182    }
183
184    @Override
185    public void run() {
186      try {
187        doWaiting();
188      } catch (InterruptedException e) {
189        throw new RuntimeException("Failed wait", e);
190      }
191      LOG.info("Exiting " + getName());
192    }
193
194    void doWaiting() throws InterruptedException {
195      try {
196        for (;;) {
197          if (MetaTableLocator.waitMetaRegionLocation(watcher, 10000) != null) {
198            break;
199          }
200        }
201      } catch (NotAllMetaRegionsOnlineException e) {
202        // Ignore
203      }
204    }
205  }
206}