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