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(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().setLimit(5).build(); 120 final List<RegionReplicaInfo> rows; 121 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 122 rows = IterableUtils.toList(results); 123 } 124 assertThat(rows, 125 contains(hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",,"))), 126 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",20000000"))), 127 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",40000000"))), 128 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",60000000"))), 129 hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",80000000"))))); 130 } 131 132 @Test 133 public void regionStateFilter() { 134 final String namespaceName = testNameRule.getMethodName(); 135 final TableName foo = TableName.valueOf(namespaceName, "foo"); 136 final TableName bar = TableName.valueOf(namespaceName, "bar"); 137 138 createNamespace(namespaceName) 139 .thenCompose(_void1 -> CompletableFuture.allOf( 140 createTable(foo, 2).thenCompose(_void2 -> admin.disableTable(foo)), createTable(bar, 2))) 141 .join(); 142 143 final HttpServletRequest request = new MockRequestBuilder().setLimit(10_000) 144 .setRegionState(RegionState.State.OPEN).setTable(namespaceName).build(); 145 final List<RegionReplicaInfo> rows; 146 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 147 rows = IterableUtils.toList(results); 148 } 149 assertThat(rows, 150 contains(hasProperty("row", bytesAsStringBinary(startsWith(bar.toString() + ",,"))), 151 hasProperty("row", bytesAsStringBinary(startsWith(bar.toString() + ",80000000"))))); 152 } 153 154 @Test 155 public void scanTableFilter() { 156 final String namespaceName = testNameRule.getMethodName(); 157 final TableName a = TableName.valueOf("a"); 158 final TableName b = TableName.valueOf(namespaceName, "b"); 159 160 CompletableFuture 161 .allOf(createTable(a), createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) 162 .join(); 163 164 final HttpServletRequest request = new MockRequestBuilder().setTable(namespaceName).build(); 165 final List<RegionReplicaInfo> rows; 166 try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { 167 rows = IterableUtils.toList(results); 168 } 169 assertThat(rows, contains(hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), 170 hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); 171 } 172 173 @Test 174 public void paginateWithReplicas() { 175 final String namespaceName = testNameRule.getMethodName(); 176 final TableName a = TableName.valueOf("a"); 177 final TableName b = TableName.valueOf(namespaceName, "b"); 178 179 CompletableFuture.allOf(createTableWithReplicas(a, 2), 180 createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))).join(); 181 182 final HttpServletRequest request1 = new MockRequestBuilder().setLimit(2).build(); 183 final List<RegionReplicaInfo> rows1; 184 try (final MetaBrowser.Results results = new MetaBrowser(connection, request1).getResults()) { 185 rows1 = IterableUtils.toList(results); 186 } 187 assertThat(rows1, 188 contains( 189 allOf(hasProperty("regionName", bytesAsStringBinary(startsWith(a + ",,"))), 190 hasProperty("replicaId", equalTo(0))), 191 allOf(hasProperty("regionName", bytesAsStringBinary(startsWith(a + ",,"))), 192 hasProperty("replicaId", equalTo(1))))); 193 194 final HttpServletRequest request2 = new MockRequestBuilder().setLimit(2) 195 .setStart(MetaBrowser.buildStartParamFrom(rows1.get(rows1.size() - 1).getRow())).build(); 196 final List<RegionReplicaInfo> rows2; 197 try (final MetaBrowser.Results results = new MetaBrowser(connection, request2).getResults()) { 198 rows2 = IterableUtils.toList(results); 199 } 200 assertThat(rows2, contains(hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), 201 hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); 202 } 203 204 @Test 205 public void paginateWithTableFilter() { 206 final String namespaceName = testNameRule.getMethodName(); 207 final TableName a = TableName.valueOf("a"); 208 final TableName b = TableName.valueOf(namespaceName, "b"); 209 210 CompletableFuture 211 .allOf(createTable(a), createNamespace(namespaceName).thenCompose(_void -> createTable(b, 5))) 212 .join(); 213 214 final HttpServletRequest request1 = 215 new MockRequestBuilder().setLimit(2).setTable(namespaceName).build(); 216 final List<RegionReplicaInfo> rows1; 217 try (final MetaBrowser.Results results = new MetaBrowser(connection, request1).getResults()) { 218 rows1 = IterableUtils.toList(results); 219 } 220 assertThat(rows1, contains(hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), 221 hasProperty("row", bytesAsStringBinary(startsWith(b + ",33333333"))))); 222 223 final HttpServletRequest request2 = new MockRequestBuilder().setLimit(2).setTable(namespaceName) 224 .setStart(MetaBrowser.buildStartParamFrom(rows1.get(rows1.size() - 1).getRow())).build(); 225 final List<RegionReplicaInfo> rows2; 226 try (final MetaBrowser.Results results = new MetaBrowser(connection, request2).getResults()) { 227 rows2 = IterableUtils.toList(results); 228 } 229 assertThat(rows2, contains(hasProperty("row", bytesAsStringBinary(startsWith(b + ",66666666"))), 230 hasProperty("row", bytesAsStringBinary(startsWith(b + ",99999999"))))); 231 232 final HttpServletRequest request3 = new MockRequestBuilder().setLimit(2).setTable(namespaceName) 233 .setStart(MetaBrowser.buildStartParamFrom(rows2.get(rows2.size() - 1).getRow())).build(); 234 final List<RegionReplicaInfo> rows3; 235 try (final MetaBrowser.Results results = new MetaBrowser(connection, request3).getResults()) { 236 rows3 = IterableUtils.toList(results); 237 } 238 assertThat(rows3, 239 contains(hasProperty("row", bytesAsStringBinary(startsWith(b + ",cccccccc"))))); 240 } 241 242 private ColumnFamilyDescriptor columnFamilyDescriptor() { 243 return ColumnFamilyDescriptorBuilder.of("f1"); 244 } 245 246 private TableDescriptor tableDescriptor(final TableName tableName) { 247 return TableDescriptorBuilder.newBuilder(tableName).setColumnFamily(columnFamilyDescriptor()) 248 .build(); 249 } 250 251 private TableDescriptor tableDescriptor(final TableName tableName, final int replicaCount) { 252 return TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(replicaCount) 253 .setColumnFamily(columnFamilyDescriptor()).build(); 254 } 255 256 private CompletableFuture<Void> createTable(final TableName tableName) { 257 return admin.createTable(tableDescriptor(tableName)); 258 } 259 260 private CompletableFuture<Void> createTable(final TableName tableName, final int splitCount) { 261 return admin.createTable(tableDescriptor(tableName), 262 new RegionSplitter.HexStringSplit().split(splitCount)); 263 } 264 265 private CompletableFuture<Void> createTableWithReplicas(final TableName tableName, 266 final int replicaCount) { 267 return admin.createTable(tableDescriptor(tableName, replicaCount)); 268 } 269 270 private CompletableFuture<Void> createNamespace(final String namespace) { 271 final NamespaceDescriptor descriptor = NamespaceDescriptor.create(namespace).build(); 272 return admin.createNamespace(descriptor); 273 } 274 275 /** 276 * Helper for mocking an {@link HttpServletRequest} relevant to the test. 277 */ 278 static class MockRequestBuilder { 279 280 private String limit = null; 281 private String regionState = null; 282 private String start = null; 283 private String table = null; 284 285 public MockRequestBuilder setLimit(final int value) { 286 this.limit = Integer.toString(value); 287 return this; 288 } 289 290 public MockRequestBuilder setLimit(final String value) { 291 this.limit = value; 292 return this; 293 } 294 295 public MockRequestBuilder setRegionState(final RegionState.State value) { 296 this.regionState = value.toString(); 297 return this; 298 } 299 300 public MockRequestBuilder setRegionState(final String value) { 301 this.regionState = value; 302 return this; 303 } 304 305 public MockRequestBuilder setStart(final String value) { 306 this.start = value; 307 return this; 308 } 309 310 public MockRequestBuilder setTable(final String value) { 311 this.table = value; 312 return this; 313 } 314 315 public HttpServletRequest build() { 316 final HttpServletRequest request = mock(HttpServletRequest.class); 317 when(request.getRequestURI()).thenReturn("/table.jsp"); 318 when(request.getParameter("name")).thenReturn("hbase%3Ameta"); 319 320 when(request.getParameter("scan_limit")).thenReturn(limit); 321 when(request.getParameter("scan_region_state")).thenReturn(regionState); 322 when(request.getParameter("scan_start")).thenReturn(start); 323 when(request.getParameter("scan_table")).thenReturn(table); 324 325 return request; 326 } 327 } 328}