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.master.webapp; 019 020import static org.apache.hadoop.hbase.client.hamcrest.BytesMatchers.bytesAsStringBinary; 021import static org.hamcrest.MatcherAssert.assertThat; 022import static org.hamcrest.Matchers.allOf; 023import static org.hamcrest.Matchers.contains; 024import static org.hamcrest.Matchers.equalTo; 025import static org.hamcrest.Matchers.hasProperty; 026import static org.hamcrest.Matchers.startsWith; 027import static org.mockito.Mockito.mock; 028import static org.mockito.Mockito.when; 029import java.util.List; 030import java.util.concurrent.CompletableFuture; 031import javax.servlet.http.HttpServletRequest; 032import org.apache.hadoop.hbase.ClearUserNamespacesAndTablesRule; 033import org.apache.hadoop.hbase.ConnectionRule; 034import org.apache.hadoop.hbase.HBaseClassTestRule; 035import org.apache.hadoop.hbase.MiniClusterRule; 036import org.apache.hadoop.hbase.NamespaceDescriptor; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.AsyncAdmin; 039import org.apache.hadoop.hbase.client.AsyncConnection; 040import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 041import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 042import org.apache.hadoop.hbase.client.TableDescriptor; 043import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 044import org.apache.hadoop.hbase.master.RegionState; 045import org.apache.hadoop.hbase.testclassification.MasterTests; 046import org.apache.hadoop.hbase.testclassification.MediumTests; 047import org.apache.hadoop.hbase.util.RegionSplitter; 048import org.junit.Before; 049import org.junit.ClassRule; 050import org.junit.Rule; 051import org.junit.Test; 052import org.junit.experimental.categories.Category; 053import org.junit.rules.RuleChain; 054import org.junit.rules.TestName; 055import org.junit.rules.TestRule; 056import org.apache.hbase.thirdparty.org.apache.commons.collections4.IterableUtils; 057 058/** 059 * Cluster-backed correctness tests for the functionality provided by {@link MetaBrowser}. 060 */ 061@Category({ MasterTests.class, MediumTests.class}) 062public class TestMetaBrowser { 063 064 @ClassRule 065 public static final HBaseClassTestRule testRule = 066 HBaseClassTestRule.forClass(TestMetaBrowser.class); 067 @ClassRule 068 public static final MiniClusterRule miniClusterRule = MiniClusterRule.newBuilder().build(); 069 070 private final ConnectionRule connectionRule = 071 new ConnectionRule(miniClusterRule::createConnection); 072 private final ClearUserNamespacesAndTablesRule clearUserNamespacesAndTablesRule = 073 new ClearUserNamespacesAndTablesRule(connectionRule::getConnection); 074 075 @Rule 076 public TestRule rule = RuleChain.outerRule(connectionRule) 077 .around(clearUserNamespacesAndTablesRule); 078 079 @Rule 080 public TestName testNameRule = new TestName(); 081 082 private AsyncConnection connection; 083 private AsyncAdmin admin; 084 085 @Before 086 public void before() { 087 connection = connectionRule.getConnection(); 088 admin = connection.getAdmin(); 089 } 090 091 @Test 092 public void noFilters() { 093 final String namespaceName = testNameRule.getMethodName(); 094 final TableName a = TableName.valueOf("a"); 095 final TableName b = TableName.valueOf(namespaceName, "b"); 096 097 CompletableFuture.allOf( 098 createTable(a), 099 createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) 100 .join(); 101 102 final HttpServletRequest request = new MockRequestBuilder().build(); 103 final List<RegionReplicaInfo> rows; 104 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 105 rows = IterableUtils.toList(results); 106 } 107 assertThat(rows, contains( 108 hasProperty("row", bytesAsStringBinary(startsWith(a + ",,"))), 109 hasProperty("row", bytesAsStringBinary(startsWith("hbase:namespace,,"))), 110 hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), 111 hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); 112 } 113 114 @Test 115 public void limit() { 116 final String tableName = testNameRule.getMethodName(); 117 createTable(TableName.valueOf(tableName), 8).join(); 118 119 final HttpServletRequest request = new MockRequestBuilder() 120 .setLimit(5) 121 .build(); 122 final List<RegionReplicaInfo> rows; 123 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 124 rows = IterableUtils.toList(results); 125 } 126 assertThat(rows, contains( 127 hasProperty("row", bytesAsStringBinary(startsWith("hbase:namespace,,"))), 128 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",,"))), 129 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",20000000"))), 130 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",40000000"))), 131 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",60000000"))))); 132 } 133 134 @Test 135 public void regionStateFilter() { 136 final String namespaceName = testNameRule.getMethodName(); 137 final TableName foo = TableName.valueOf(namespaceName, "foo"); 138 final TableName bar = TableName.valueOf(namespaceName, "bar"); 139 140 createNamespace(namespaceName) 141 .thenCompose(_void1 -> CompletableFuture.allOf( 142 createTable(foo, 2).thenCompose(_void2 -> admin.disableTable(foo)), 143 createTable(bar, 2))) 144 .join(); 145 146 final HttpServletRequest request = new MockRequestBuilder() 147 .setLimit(10_000) 148 .setRegionState(RegionState.State.OPEN) 149 .setTable(namespaceName) 150 .build(); 151 final List<RegionReplicaInfo> rows; 152 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 153 rows = IterableUtils.toList(results); 154 } 155 assertThat(rows, contains( 156 hasProperty("row", bytesAsStringBinary(startsWith(bar.toString() + ",,"))), 157 hasProperty("row", bytesAsStringBinary(startsWith(bar.toString() + ",80000000"))))); 158 } 159 160 @Test 161 public void scanTableFilter() { 162 final String namespaceName = testNameRule.getMethodName(); 163 final TableName a = TableName.valueOf("a"); 164 final TableName b = TableName.valueOf(namespaceName, "b"); 165 166 CompletableFuture.allOf( 167 createTable(a), 168 createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) 169 .join(); 170 171 final HttpServletRequest request = new MockRequestBuilder() 172 .setTable(namespaceName) 173 .build(); 174 final List<RegionReplicaInfo> rows; 175 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 176 rows = IterableUtils.toList(results); 177 } 178 assertThat(rows, contains( 179 hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), 180 hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); 181 } 182 183 @Test 184 public void paginateWithReplicas() { 185 final String namespaceName = testNameRule.getMethodName(); 186 final TableName a = TableName.valueOf("a"); 187 final TableName b = TableName.valueOf(namespaceName, "b"); 188 189 CompletableFuture.allOf( 190 createTableWithReplicas(a, 2), 191 createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) 192 .join(); 193 194 final HttpServletRequest request1 = new MockRequestBuilder() 195 .setLimit(2) 196 .build(); 197 final List<RegionReplicaInfo> rows1; 198 try (final MetaBrowser.Results results = new MetaBrowser(connection, request1).getResults()) { 199 rows1 = IterableUtils.toList(results); 200 } 201 assertThat(rows1, contains( 202 allOf( 203 hasProperty("regionName", bytesAsStringBinary(startsWith(a + ",,"))), 204 hasProperty("replicaId", equalTo(0))), 205 allOf( 206 hasProperty("regionName", bytesAsStringBinary(startsWith(a + ",,"))), 207 hasProperty("replicaId", equalTo(1))))); 208 209 final HttpServletRequest request2 = new MockRequestBuilder() 210 .setLimit(2) 211 .setStart(MetaBrowser.buildStartParamFrom(rows1.get(rows1.size() - 1).getRow())) 212 .build(); 213 final List<RegionReplicaInfo> rows2; 214 try (final MetaBrowser.Results results = new MetaBrowser(connection, request2).getResults()) { 215 rows2 = IterableUtils.toList(results); 216 } 217 assertThat(rows2, contains( 218 hasProperty("row", bytesAsStringBinary(startsWith("hbase:namespace,,"))), 219 hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))))); 220 221 final HttpServletRequest request3 = new MockRequestBuilder() 222 .setLimit(2) 223 .setStart(MetaBrowser.buildStartParamFrom(rows2.get(rows2.size() - 1).getRow())) 224 .build(); 225 final List<RegionReplicaInfo> rows3; 226 try (final MetaBrowser.Results results = new MetaBrowser(connection, request3).getResults()) { 227 rows3 = IterableUtils.toList(results); 228 } 229 assertThat(rows3, contains( 230 hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); 231 } 232 233 @Test 234 public void paginateWithTableFilter() { 235 final String namespaceName = testNameRule.getMethodName(); 236 final TableName a = TableName.valueOf("a"); 237 final TableName b = TableName.valueOf(namespaceName, "b"); 238 239 CompletableFuture.allOf( 240 createTable(a), 241 createNamespace(namespaceName).thenCompose(_void -> createTable(b, 5))) 242 .join(); 243 244 final HttpServletRequest request1 = new MockRequestBuilder() 245 .setLimit(2) 246 .setTable(namespaceName) 247 .build(); 248 final List<RegionReplicaInfo> rows1; 249 try (final MetaBrowser.Results results = new MetaBrowser(connection, request1).getResults()) { 250 rows1 = IterableUtils.toList(results); 251 } 252 assertThat(rows1, contains( 253 hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), 254 hasProperty("row", bytesAsStringBinary(startsWith(b + ",33333333"))))); 255 256 final HttpServletRequest request2 = new MockRequestBuilder() 257 .setLimit(2) 258 .setTable(namespaceName) 259 .setStart(MetaBrowser.buildStartParamFrom(rows1.get(rows1.size() - 1).getRow())) 260 .build(); 261 final List<RegionReplicaInfo> rows2; 262 try (final MetaBrowser.Results results = new MetaBrowser(connection, request2).getResults()) { 263 rows2 = IterableUtils.toList(results); 264 } 265 assertThat(rows2, contains( 266 hasProperty("row", bytesAsStringBinary(startsWith(b + ",66666666"))), 267 hasProperty("row", bytesAsStringBinary(startsWith(b + ",99999999"))))); 268 269 final HttpServletRequest request3 = new MockRequestBuilder() 270 .setLimit(2) 271 .setTable(namespaceName) 272 .setStart(MetaBrowser.buildStartParamFrom(rows2.get(rows2.size() - 1).getRow())) 273 .build(); 274 final List<RegionReplicaInfo> rows3; 275 try (final MetaBrowser.Results results = new MetaBrowser(connection, request3).getResults()) { 276 rows3 = IterableUtils.toList(results); 277 } 278 assertThat(rows3, contains( 279 hasProperty("row", bytesAsStringBinary(startsWith(b + ",cccccccc"))))); 280 } 281 282 private ColumnFamilyDescriptor columnFamilyDescriptor() { 283 return ColumnFamilyDescriptorBuilder.of("f1"); 284 } 285 286 private TableDescriptor tableDescriptor(final TableName tableName) { 287 return TableDescriptorBuilder.newBuilder(tableName) 288 .setColumnFamily(columnFamilyDescriptor()) 289 .build(); 290 } 291 292 private TableDescriptor tableDescriptor(final TableName tableName, final int replicaCount) { 293 return TableDescriptorBuilder.newBuilder(tableName) 294 .setRegionReplication(replicaCount) 295 .setColumnFamily(columnFamilyDescriptor()) 296 .build(); 297 } 298 299 private CompletableFuture<Void> createTable(final TableName tableName) { 300 return admin.createTable(tableDescriptor(tableName)); 301 } 302 303 private CompletableFuture<Void> createTable(final TableName tableName, final int splitCount) { 304 return admin.createTable( 305 tableDescriptor(tableName), 306 new RegionSplitter.HexStringSplit().split(splitCount)); 307 } 308 309 private CompletableFuture<Void> createTableWithReplicas(final TableName tableName, 310 final int replicaCount) { 311 return admin.createTable(tableDescriptor(tableName, replicaCount)); 312 } 313 314 private CompletableFuture<Void> createNamespace(final String namespace) { 315 final NamespaceDescriptor descriptor = NamespaceDescriptor.create(namespace).build(); 316 return admin.createNamespace(descriptor); 317 } 318 319 /** 320 * Helper for mocking an {@link HttpServletRequest} relevant to the test. 321 */ 322 static class MockRequestBuilder { 323 324 private String limit = null; 325 private String regionState = null; 326 private String start = null; 327 private String table = null; 328 329 public MockRequestBuilder setLimit(final int value) { 330 this.limit = Integer.toString(value); 331 return this; 332 } 333 334 public MockRequestBuilder setLimit(final String value) { 335 this.limit = value; 336 return this; 337 } 338 339 public MockRequestBuilder setRegionState(final RegionState.State value) { 340 this.regionState = value.toString(); 341 return this; 342 } 343 344 public MockRequestBuilder setRegionState(final String value) { 345 this.regionState = value; 346 return this; 347 } 348 349 public MockRequestBuilder setStart(final String value) { 350 this.start = value; 351 return this; 352 } 353 354 public MockRequestBuilder setTable(final String value) { 355 this.table = value; 356 return this; 357 } 358 359 public HttpServletRequest build() { 360 final HttpServletRequest request = mock(HttpServletRequest.class); 361 when(request.getRequestURI()).thenReturn("/table.jsp"); 362 when(request.getParameter("name")).thenReturn("hbase%3Ameta"); 363 364 when(request.getParameter("scan_limit")).thenReturn(limit); 365 when(request.getParameter("scan_region_state")).thenReturn(regionState); 366 when(request.getParameter("scan_start")).thenReturn(start); 367 when(request.getParameter("scan_table")).thenReturn(table); 368 369 return request; 370 } 371 } 372}