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.assertEquals; 021import static org.junit.Assert.assertNotEquals; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027import org.apache.commons.io.IOUtils; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseConfiguration; 031import org.apache.hadoop.hbase.HBaseTestingUtil; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.testclassification.ClientTests; 034import org.apache.hadoop.hbase.testclassification.MediumTests; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.apache.hadoop.hbase.util.Threads; 037import org.junit.AfterClass; 038import org.junit.BeforeClass; 039import org.junit.ClassRule; 040import org.junit.Test; 041import org.junit.experimental.categories.Category; 042 043@Category({ MediumTests.class, ClientTests.class }) 044public class TestRegionLocationCaching { 045 046 @ClassRule 047 public static final HBaseClassTestRule CLASS_RULE = 048 HBaseClassTestRule.forClass(TestRegionLocationCaching.class); 049 050 private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 051 private static int SLAVES = 1; 052 private static TableName TABLE_NAME = TableName.valueOf("TestRegionLocationCaching"); 053 private static byte[] FAMILY = Bytes.toBytes("testFamily"); 054 private static byte[] QUALIFIER = Bytes.toBytes("testQualifier"); 055 056 @BeforeClass 057 public static void setUpBeforeClass() throws Exception { 058 TEST_UTIL.startMiniCluster(SLAVES); 059 TEST_UTIL.createTable(TABLE_NAME, new byte[][] { FAMILY }); 060 TEST_UTIL.waitUntilAllRegionsAssigned(TABLE_NAME); 061 } 062 063 @AfterClass 064 public static void tearDownAfterClass() throws Exception { 065 TEST_UTIL.shutdownMiniCluster(); 066 } 067 068 @Test 069 public void testCachingForHTableSinglePut() throws Exception { 070 byte[] row = Bytes.toBytes("htable_single_put"); 071 byte[] value = Bytes.toBytes("value"); 072 073 Put put = new Put(row); 074 put.addColumn(FAMILY, QUALIFIER, value); 075 076 try (Table table = TEST_UTIL.getConnection().getTable(TABLE_NAME)) { 077 table.put(put); 078 } 079 080 checkRegionLocationIsCached(TABLE_NAME, TEST_UTIL.getConnection()); 081 checkExistence(TABLE_NAME, row, FAMILY, QUALIFIER); 082 } 083 084 @Test 085 public void testCachingForHTableMultiPut() throws Exception { 086 List<Put> multiput = new ArrayList<Put>(); 087 for (int i = 0; i < 10; i++) { 088 Put put = new Put(Bytes.toBytes("htable_multi_put" + i)); 089 byte[] value = Bytes.toBytes("value_" + i); 090 put.addColumn(FAMILY, QUALIFIER, value); 091 multiput.add(put); 092 } 093 094 try (Table table = TEST_UTIL.getConnection().getTable(TABLE_NAME)) { 095 table.put(multiput); 096 } 097 checkRegionLocationIsCached(TABLE_NAME, TEST_UTIL.getConnection()); 098 for (int i = 0; i < 10; i++) { 099 checkExistence(TABLE_NAME, Bytes.toBytes("htable_multi_put" + i), FAMILY, QUALIFIER); 100 } 101 } 102 103 /** 104 * Method to check whether the cached region location is non-empty for the given table. It repeats 105 * the same check several times as clearing of cache by some async operations may not reflect 106 * immediately. 107 */ 108 private void checkRegionLocationIsCached(final TableName tableName, final Connection conn) 109 throws InterruptedException, IOException { 110 for (int count = 0; count < 50; count++) { 111 int number = ((AsyncConnectionImpl) conn.toAsyncConnection()).getLocator() 112 .getNumberOfCachedRegionLocations(tableName); 113 assertNotEquals("Expected non-zero number of cached region locations", 0, number); 114 Thread.sleep(100); 115 } 116 } 117 118 /** 119 * Method to check whether the cached region location is empty for the given table. It repeats the 120 * same check several times as clearing of cache by some async operations may not reflect 121 * immediately. 122 */ 123 private void checkRegionLocationIsNotCached(final TableName tableName, final Connection conn) 124 throws InterruptedException { 125 for (int count = 0; count < 50; count++) { 126 int number = ((AsyncConnectionImpl) conn.toAsyncConnection()).getLocator() 127 .getNumberOfCachedRegionLocations(tableName); 128 assertEquals("Expected zero number of cached region locations", 0, number); 129 Thread.sleep(100); 130 } 131 } 132 133 /** 134 * Method to check whether the passed row exists in the given table 135 */ 136 private static void checkExistence(final TableName tableName, final byte[] row, 137 final byte[] family, final byte[] qualifier) throws Exception { 138 // verify that the row exists 139 Result r; 140 Get get = new Get(row); 141 get.addColumn(family, qualifier); 142 int nbTry = 0; 143 try (Table table = TEST_UTIL.getConnection().getTable(tableName)) { 144 do { 145 assertTrue("Failed to get row after " + nbTry + " tries", nbTry < 50); 146 nbTry++; 147 Thread.sleep(100); 148 r = table.get(get); 149 } while (r == null || r.getValue(family, qualifier) == null); 150 } 151 } 152 153 @Test 154 public void testInvalidateMetaCache() throws Throwable { 155 // There are 2 tables and 2 connections, both connection cached all region locations of all 156 // tables, 157 // after disable/delete one table using one connection, need invalidate the meta cache 158 // of the table in other connections. 159 ColumnFamilyDescriptor cfd = 160 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")).build(); 161 162 TableName tbn1 = TableName.valueOf("testInvalidateMetaCache1"); 163 TableDescriptor tbd1 = TableDescriptorBuilder.newBuilder(tbn1).setColumnFamily(cfd).build(); 164 165 TableName tbn2 = TableName.valueOf("testInvalidateMetaCache2"); 166 TableDescriptor tbd2 = TableDescriptorBuilder.newBuilder(tbn2).setColumnFamily(cfd).build(); 167 168 Configuration conf1 = HBaseConfiguration.create(TEST_UTIL.getConfiguration()); 169 conf1.setLong("hbase.client.connection.metacache.invalidate-interval.ms", 5 * 1000); 170 171 Connection conn1 = ConnectionFactory.createConnection(conf1); 172 Connection conn2 = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); 173 174 try { 175 Admin admin1 = conn1.getAdmin(); 176 admin1.createTable(tbd1); 177 admin1.createTable(tbd2); 178 conn1.getRegionLocator(tbn1).getAllRegionLocations(); 179 conn1.getRegionLocator(tbn2).getAllRegionLocations(); 180 checkRegionLocationIsCached(tbn1, conn1); 181 checkRegionLocationIsCached(tbn2, conn1); 182 183 Admin admin2 = conn2.getAdmin(); 184 admin2.disableTable(tbn1); 185 // Sleep 10s to test whether the invalidateMetaCache task could execute regularly(the interval 186 // is 5s). 187 Threads.sleep(10 * 1000); 188 checkRegionLocationIsNotCached(tbn1, conn1); 189 checkRegionLocationIsCached(tbn2, conn1); 190 191 admin2.disableTable(tbn2); 192 admin2.deleteTable(tbn2); 193 Threads.sleep(10 * 1000); 194 checkRegionLocationIsNotCached(tbn1, conn1); 195 checkRegionLocationIsNotCached(tbn2, conn1); 196 } finally { 197 IOUtils.closeQuietly(conn1, conn2); 198 } 199 } 200 201 @Test 202 public void testDisableInvalidateMetaCache() throws Throwable { 203 ColumnFamilyDescriptor cfd = 204 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")).build(); 205 206 TableName tbn1 = TableName.valueOf("testDisableInvalidateMetaCache1"); 207 TableDescriptor tbd1 = TableDescriptorBuilder.newBuilder(tbn1).setColumnFamily(cfd).build(); 208 209 TableName tbn2 = TableName.valueOf("testDisableInvalidateMetaCache2"); 210 TableDescriptor tbd2 = TableDescriptorBuilder.newBuilder(tbn2).setColumnFamily(cfd).build(); 211 212 Connection conn1 = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); 213 Connection conn2 = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); 214 215 try { 216 Admin admin1 = conn1.getAdmin(); 217 admin1.createTable(tbd1); 218 admin1.createTable(tbd2); 219 conn1.getRegionLocator(tbn1).getAllRegionLocations(); 220 conn1.getRegionLocator(tbn2).getAllRegionLocations(); 221 checkRegionLocationIsCached(tbn1, conn1); 222 checkRegionLocationIsCached(tbn2, conn1); 223 224 Admin admin2 = conn2.getAdmin(); 225 admin2.disableTable(tbn1); 226 Threads.sleep(10 * 1000); 227 checkRegionLocationIsCached(tbn1, conn1); 228 checkRegionLocationIsCached(tbn2, conn1); 229 230 admin2.disableTable(tbn2); 231 admin2.deleteTable(tbn2); 232 Threads.sleep(10 * 1000); 233 checkRegionLocationIsCached(tbn1, conn1); 234 checkRegionLocationIsCached(tbn2, conn1); 235 } finally { 236 IOUtils.closeQuietly(conn1, conn2); 237 } 238 } 239}