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.assertArrayEquals; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.util.Arrays; 026import java.util.Optional; 027import java.util.concurrent.atomic.AtomicInteger; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseTestingUtility; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.coprocessor.ObserverContext; 032import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 033import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 034import org.apache.hadoop.hbase.coprocessor.RegionObserver; 035import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; 036import org.apache.hadoop.hbase.testclassification.ClientTests; 037import org.apache.hadoop.hbase.testclassification.MediumTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.hadoop.hbase.util.Threads; 040import org.junit.After; 041import org.junit.AfterClass; 042import org.junit.Before; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Rule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.rules.TestName; 049 050import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 051 052@Category({ MediumTests.class, ClientTests.class }) 053public class TestHTableNoncedRetry { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestHTableNoncedRetry.class); 058 059 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 060 061 private static final TableName TABLE_NAME = TableName.valueOf("async"); 062 063 private static final byte[] FAMILY = Bytes.toBytes("cf"); 064 065 private static final byte[] QUALIFIER = Bytes.toBytes("cq"); 066 067 private static final byte[] QUALIFIER2 = Bytes.toBytes("cq2"); 068 069 private static final byte[] QUALIFIER3 = Bytes.toBytes("cq3"); 070 071 private static final byte[] VALUE = Bytes.toBytes("value"); 072 073 private static Connection CONN; 074 075 @Rule 076 public TestName testName = new TestName(); 077 078 private byte[] row; 079 080 private Table table; 081 082 private static final AtomicInteger CALLED = new AtomicInteger(); 083 084 private static final int SLEEP_TIME = 2000; 085 086 private static final int RPC_TIMEOUT = SLEEP_TIME / 4 * 3; // three fourths of the sleep time 087 088 // The number of miniBatchOperations that are executed in a RegionServer 089 private static int miniBatchOperationCount; 090 091 public static final class SleepOnceCP implements RegionObserver, RegionCoprocessor { 092 093 @Override 094 public Optional<RegionObserver> getRegionObserver() { 095 return Optional.of(this); 096 } 097 098 @Override 099 public void postBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c, 100 MiniBatchOperationInProgress<Mutation> miniBatchOp) { 101 // We sleep when the last of the miniBatchOperations is executed 102 if (CALLED.getAndIncrement() == miniBatchOperationCount - 1) { 103 Threads.sleepWithoutInterrupt(SLEEP_TIME); 104 } 105 } 106 } 107 108 @BeforeClass 109 public static void setUpBeforeClass() throws Exception { 110 TEST_UTIL.startMiniCluster(1); 111 TEST_UTIL.getAdmin() 112 .createTable(TableDescriptorBuilder.newBuilder(TABLE_NAME) 113 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)) 114 .setCoprocessor(SleepOnceCP.class.getName()).build()); 115 TEST_UTIL.waitTableAvailable(TABLE_NAME); 116 CONN = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); 117 } 118 119 @AfterClass 120 public static void tearDownAfterClass() throws Exception { 121 Closeables.close(CONN, true); 122 TEST_UTIL.shutdownMiniCluster(); 123 } 124 125 @Before 126 public void setUp() throws IOException, InterruptedException { 127 row = Bytes.toBytes(testName.getMethodName().replaceAll("[^0-9A-Za-z]", "_")); 128 CALLED.set(0); 129 130 table = CONN.getTable(TABLE_NAME); 131 table.setRpcTimeout(RPC_TIMEOUT); 132 } 133 134 @After 135 public void tearDown() throws Exception { 136 table.close(); 137 } 138 139 @Test 140 public void testAppend() throws IOException { 141 assertEquals(0, CALLED.get()); 142 143 miniBatchOperationCount = 1; 144 Result result = table.append(new Append(row).addColumn(FAMILY, QUALIFIER, VALUE)); 145 146 // make sure we called twice and the result is still correct 147 assertEquals(2, CALLED.get()); 148 assertArrayEquals(VALUE, result.getValue(FAMILY, QUALIFIER)); 149 } 150 151 @Test 152 public void testAppendWhenReturnResultsEqualsFalse() throws IOException { 153 assertEquals(0, CALLED.get()); 154 155 miniBatchOperationCount = 1; 156 Result result = 157 table.append(new Append(row).addColumn(FAMILY, QUALIFIER, VALUE).setReturnResults(false)); 158 159 // make sure we called twice and the result is still correct 160 assertEquals(2, CALLED.get()); 161 assertTrue(result.isEmpty()); 162 } 163 164 @Test 165 public void testIncrement() throws IOException { 166 assertEquals(0, CALLED.get()); 167 168 miniBatchOperationCount = 1; 169 long result = table.incrementColumnValue(row, FAMILY, QUALIFIER, 1L); 170 171 // make sure we called twice and the result is still correct 172 assertEquals(2, CALLED.get()); 173 assertEquals(1L, result); 174 } 175 176 @Test 177 public void testIncrementWhenReturnResultsEqualsFalse() throws IOException { 178 assertEquals(0, CALLED.get()); 179 180 miniBatchOperationCount = 1; 181 Result result = 182 table.increment(new Increment(row).addColumn(FAMILY, QUALIFIER, 1L).setReturnResults(false)); 183 184 // make sure we called twice and the result is still correct 185 assertEquals(2, CALLED.get()); 186 assertTrue(result.isEmpty()); 187 } 188 189 @Test 190 public void testIncrementInRowMutations() throws IOException { 191 assertEquals(0, CALLED.get()); 192 193 miniBatchOperationCount = 1; 194 Result result = 195 table.mutateRow(new RowMutations(row).add(new Increment(row).addColumn(FAMILY, QUALIFIER, 1L)) 196 .add((Mutation) new Delete(row).addColumn(FAMILY, QUALIFIER2))); 197 198 // make sure we called twice and the result is still correct 199 assertEquals(2, CALLED.get()); 200 assertEquals(1L, Bytes.toLong(result.getValue(FAMILY, QUALIFIER))); 201 } 202 203 @Test 204 public void testAppendInRowMutations() throws IOException { 205 assertEquals(0, CALLED.get()); 206 207 miniBatchOperationCount = 1; 208 Result result = 209 table.mutateRow(new RowMutations(row).add(new Append(row).addColumn(FAMILY, QUALIFIER, VALUE)) 210 .add((Mutation) new Delete(row).addColumn(FAMILY, QUALIFIER2))); 211 212 // make sure we called twice and the result is still correct 213 assertEquals(2, CALLED.get()); 214 assertArrayEquals(VALUE, result.getValue(FAMILY, QUALIFIER)); 215 } 216 217 @Test 218 public void testIncrementAndAppendInRowMutations() throws IOException { 219 assertEquals(0, CALLED.get()); 220 221 miniBatchOperationCount = 1; 222 Result result = 223 table.mutateRow(new RowMutations(row).add(new Increment(row).addColumn(FAMILY, QUALIFIER, 1L)) 224 .add(new Append(row).addColumn(FAMILY, QUALIFIER2, VALUE))); 225 226 // make sure we called twice and the result is still correct 227 assertEquals(2, CALLED.get()); 228 assertEquals(1L, Bytes.toLong(result.getValue(FAMILY, QUALIFIER))); 229 assertArrayEquals(VALUE, result.getValue(FAMILY, QUALIFIER2)); 230 } 231 232 @Test 233 public void testIncrementInCheckAndMutate() throws IOException { 234 assertEquals(0, CALLED.get()); 235 236 miniBatchOperationCount = 1; 237 CheckAndMutateResult result = table.checkAndMutate(CheckAndMutate.newBuilder(row) 238 .ifNotExists(FAMILY, QUALIFIER2).build(new Increment(row).addColumn(FAMILY, QUALIFIER, 1L))); 239 240 // make sure we called twice and the result is still correct 241 assertEquals(2, CALLED.get()); 242 assertTrue(result.isSuccess()); 243 assertEquals(1L, Bytes.toLong(result.getResult().getValue(FAMILY, QUALIFIER))); 244 } 245 246 @Test 247 public void testAppendInCheckAndMutate() throws IOException { 248 assertEquals(0, CALLED.get()); 249 250 miniBatchOperationCount = 1; 251 CheckAndMutateResult result = table.checkAndMutate(CheckAndMutate.newBuilder(row) 252 .ifNotExists(FAMILY, QUALIFIER2).build(new Append(row).addColumn(FAMILY, QUALIFIER, VALUE))); 253 254 // make sure we called twice and the result is still correct 255 assertEquals(2, CALLED.get()); 256 assertTrue(result.isSuccess()); 257 assertArrayEquals(VALUE, result.getResult().getValue(FAMILY, QUALIFIER)); 258 } 259 260 @Test 261 public void testIncrementInRowMutationsInCheckAndMutate() throws IOException { 262 assertEquals(0, CALLED.get()); 263 264 miniBatchOperationCount = 1; 265 CheckAndMutateResult result = 266 table.checkAndMutate(CheckAndMutate.newBuilder(row).ifNotExists(FAMILY, QUALIFIER3) 267 .build(new RowMutations(row).add(new Increment(row).addColumn(FAMILY, QUALIFIER, 1L)) 268 .add((Mutation) new Delete(row).addColumn(FAMILY, QUALIFIER2)))); 269 270 // make sure we called twice and the result is still correct 271 assertEquals(2, CALLED.get()); 272 assertTrue(result.isSuccess()); 273 assertEquals(1L, Bytes.toLong(result.getResult().getValue(FAMILY, QUALIFIER))); 274 } 275 276 @Test 277 public void testAppendInRowMutationsInCheckAndMutate() throws IOException { 278 assertEquals(0, CALLED.get()); 279 280 miniBatchOperationCount = 1; 281 CheckAndMutateResult result = 282 table.checkAndMutate(CheckAndMutate.newBuilder(row).ifNotExists(FAMILY, QUALIFIER3) 283 .build(new RowMutations(row).add(new Append(row).addColumn(FAMILY, QUALIFIER, VALUE)) 284 .add((Mutation) new Delete(row).addColumn(FAMILY, QUALIFIER2)))); 285 286 // make sure we called twice and the result is still correct 287 assertEquals(2, CALLED.get()); 288 assertTrue(result.isSuccess()); 289 assertArrayEquals(VALUE, result.getResult().getValue(FAMILY, QUALIFIER)); 290 } 291 292 @Test 293 public void testIncrementAndAppendInRowMutationsInCheckAndMutate() throws IOException { 294 assertEquals(0, CALLED.get()); 295 296 miniBatchOperationCount = 1; 297 CheckAndMutateResult result = 298 table.checkAndMutate(CheckAndMutate.newBuilder(row).ifNotExists(FAMILY, QUALIFIER3) 299 .build(new RowMutations(row).add(new Increment(row).addColumn(FAMILY, QUALIFIER, 1L)) 300 .add(new Append(row).addColumn(FAMILY, QUALIFIER2, VALUE)))); 301 302 // make sure we called twice and the result is still correct 303 assertEquals(2, CALLED.get()); 304 assertTrue(result.isSuccess()); 305 assertEquals(1L, Bytes.toLong(result.getResult().getValue(FAMILY, QUALIFIER))); 306 assertArrayEquals(VALUE, result.getResult().getValue(FAMILY, QUALIFIER2)); 307 } 308 309 @Test 310 public void testBatch() throws IOException, InterruptedException { 311 byte[] row2 = Bytes.toBytes(Bytes.toString(row) + "2"); 312 byte[] row3 = Bytes.toBytes(Bytes.toString(row) + "3"); 313 byte[] row4 = Bytes.toBytes(Bytes.toString(row) + "4"); 314 byte[] row5 = Bytes.toBytes(Bytes.toString(row) + "5"); 315 byte[] row6 = Bytes.toBytes(Bytes.toString(row) + "6"); 316 317 assertEquals(0, CALLED.get()); 318 319 miniBatchOperationCount = 6; 320 Object[] results = new Object[6]; 321 table.batch(Arrays.asList(new Append(row).addColumn(FAMILY, QUALIFIER, VALUE), 322 new Increment(row2).addColumn(FAMILY, QUALIFIER, 1L), 323 new RowMutations(row3).add(new Increment(row3).addColumn(FAMILY, QUALIFIER, 1L)) 324 .add(new Append(row3).addColumn(FAMILY, QUALIFIER2, VALUE)), 325 CheckAndMutate.newBuilder(row4).ifNotExists(FAMILY, QUALIFIER2) 326 .build(new Increment(row4).addColumn(FAMILY, QUALIFIER, 1L)), 327 CheckAndMutate.newBuilder(row5).ifNotExists(FAMILY, QUALIFIER2) 328 .build(new Append(row5).addColumn(FAMILY, QUALIFIER, VALUE)), 329 CheckAndMutate.newBuilder(row6).ifNotExists(FAMILY, QUALIFIER3) 330 .build(new RowMutations(row6).add(new Increment(row6).addColumn(FAMILY, QUALIFIER, 1L)) 331 .add(new Append(row6).addColumn(FAMILY, QUALIFIER2, VALUE)))), 332 results); 333 334 // make sure we called twice and the result is still correct 335 336 // should be called 12 times as 6 miniBatchOperations are called twice 337 assertEquals(12, CALLED.get()); 338 339 assertArrayEquals(VALUE, ((Result) results[0]).getValue(FAMILY, QUALIFIER)); 340 341 assertEquals(1L, Bytes.toLong(((Result) results[1]).getValue(FAMILY, QUALIFIER))); 342 343 assertEquals(1L, Bytes.toLong(((Result) results[2]).getValue(FAMILY, QUALIFIER))); 344 assertArrayEquals(VALUE, ((Result) results[2]).getValue(FAMILY, QUALIFIER2)); 345 346 CheckAndMutateResult result; 347 348 result = (CheckAndMutateResult) results[3]; 349 assertTrue(result.isSuccess()); 350 assertEquals(1L, Bytes.toLong(result.getResult().getValue(FAMILY, QUALIFIER))); 351 352 result = (CheckAndMutateResult) results[4]; 353 assertTrue(result.isSuccess()); 354 assertArrayEquals(VALUE, result.getResult().getValue(FAMILY, QUALIFIER)); 355 356 result = (CheckAndMutateResult) results[5]; 357 assertTrue(result.isSuccess()); 358 assertEquals(1L, Bytes.toLong(result.getResult().getValue(FAMILY, QUALIFIER))); 359 assertArrayEquals(VALUE, result.getResult().getValue(FAMILY, QUALIFIER2)); 360 } 361}