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