/** * Copyright The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.client; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Waiter; import org.apache.hadoop.hbase.regionserver.HRegionServer; import org.apache.hadoop.hbase.testclassification.ClientTests; import org.apache.hadoop.hbase.testclassification.LargeTests; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TestName; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @Category({ LargeTests.class, ClientTests.class }) public class TestHTableMultiplexerFlushCache { private static final Log LOG = LogFactory.getLog(TestHTableMultiplexerFlushCache.class); private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static byte[] FAMILY = Bytes.toBytes("testFamily"); private static byte[] QUALIFIER1 = Bytes.toBytes("testQualifier_1"); private static byte[] QUALIFIER2 = Bytes.toBytes("testQualifier_2"); private static byte[] VALUE1 = Bytes.toBytes("testValue1"); private static byte[] VALUE2 = Bytes.toBytes("testValue2"); private static int SLAVES = 3; private static int PER_REGIONSERVER_QUEUE_SIZE = 100000; @Rule public TestName name = new TestName(); /** * @throws java.lang.Exception */ @BeforeClass public static void setUpBeforeClass() throws Exception { TEST_UTIL.startMiniCluster(SLAVES); } /** * @throws java.lang.Exception */ @AfterClass public static void tearDownAfterClass() throws Exception { TEST_UTIL.shutdownMiniCluster(); } private static void checkExistence(final Table htable, final byte[] row, final byte[] family, final byte[] quality, final byte[] value) throws Exception { // verify that the Get returns the correct result TEST_UTIL.waitFor(30000, new Waiter.Predicate<Exception>() { @Override public boolean evaluate() throws Exception { Result r; Get get = new Get(row); get.addColumn(family, quality); r = htable.get(get); return r != null && r.getValue(family, quality) != null && Bytes.toStringBinary(value).equals( Bytes.toStringBinary(r.getValue(family, quality))); } }); } @Test public void testOnRegionChange() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); final int NUM_REGIONS = 10; Table htable = TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, 3, Bytes.toBytes("aaaaa"), Bytes.toBytes("zzzzz"), NUM_REGIONS); HTableMultiplexer multiplexer = new HTableMultiplexer(TEST_UTIL.getConfiguration(), PER_REGIONSERVER_QUEUE_SIZE); try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(tableName)) { byte[][] startRows = r.getStartKeys(); byte[] row = startRows[1]; assertTrue("2nd region should not start with empty row", row != null && row.length > 0); Put put = new Put(row).addColumn(FAMILY, QUALIFIER1, VALUE1); assertTrue("multiplexer.put returns", multiplexer.put(tableName, put)); checkExistence(htable, row, FAMILY, QUALIFIER1, VALUE1); // Now let's shutdown the regionserver and let regions moved to other servers. HRegionLocation loc = r.getRegionLocation(row); MiniHBaseCluster hbaseCluster = TEST_UTIL.getHBaseCluster(); hbaseCluster.stopRegionServer(loc.getServerName()); TEST_UTIL.waitUntilAllRegionsAssigned(tableName); // put with multiplexer. put = new Put(row).addColumn(FAMILY, QUALIFIER2, VALUE2); assertTrue("multiplexer.put returns", multiplexer.put(tableName, put)); checkExistence(htable, row, FAMILY, QUALIFIER2, VALUE2); } } @Test public void testOnRegionMove() throws Exception { // This test is doing near exactly the same thing that testOnRegionChange but avoiding the // potential to get a ConnectionClosingException. By moving the region, we can be certain that // the connection is still valid and that the implementation is correctly handling an invalid // Region cache (and not just tearing down the entire connection). final TableName tableName = TableName.valueOf(name.getMethodName()); final int NUM_REGIONS = 10; Table htable = TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, 3, Bytes.toBytes("aaaaa"), Bytes.toBytes("zzzzz"), NUM_REGIONS); HTableMultiplexer multiplexer = new HTableMultiplexer(TEST_UTIL.getConfiguration(), PER_REGIONSERVER_QUEUE_SIZE); final RegionLocator regionLocator = TEST_UTIL.getConnection().getRegionLocator(tableName); Pair<byte[][],byte[][]> startEndRows = regionLocator.getStartEndKeys(); byte[] row = startEndRows.getFirst()[1]; assertTrue("2nd region should not start with empty row", row != null && row.length > 0); Put put = new Put(row).addColumn(FAMILY, QUALIFIER1, VALUE1); assertTrue("multiplexer.put returns", multiplexer.put(tableName, put)); checkExistence(htable, row, FAMILY, QUALIFIER1, VALUE1); final HRegionLocation loc = regionLocator.getRegionLocation(row); final MiniHBaseCluster hbaseCluster = TEST_UTIL.getHBaseCluster(); // The current server for the region we're writing to final ServerName originalServer = loc.getServerName(); ServerName newServer = null; // Find a new server to move that region to for (int i = 0; i < SLAVES; i++) { HRegionServer rs = hbaseCluster.getRegionServer(0); if (!rs.getServerName().equals(originalServer.getServerName())) { newServer = rs.getServerName(); break; } } assertNotNull("Did not find a new RegionServer to use", newServer); // Move the region LOG.info("Moving " + loc.getRegionInfo().getEncodedName() + " from " + originalServer + " to " + newServer); TEST_UTIL.getAdmin().move(loc.getRegionInfo().getEncodedNameAsBytes(), Bytes.toBytes(newServer.getServerName())); TEST_UTIL.waitUntilAllRegionsAssigned(tableName); // Send a new Put put = new Put(row).addColumn(FAMILY, QUALIFIER2, VALUE2); assertTrue("multiplexer.put returns", multiplexer.put(tableName, put)); // We should see the update make it to the new server eventually checkExistence(htable, row, FAMILY, QUALIFIER2, VALUE2); } }