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.hamcrest.CoreMatchers.instanceOf; 021import static org.junit.Assert.assertArrayEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertThat; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.List; 031import java.util.concurrent.CompletableFuture; 032import java.util.concurrent.ExecutionException; 033import java.util.concurrent.ThreadLocalRandom; 034import java.util.stream.Collectors; 035import java.util.stream.IntStream; 036import org.apache.hadoop.hbase.HBaseClassTestRule; 037import org.apache.hadoop.hbase.HBaseTestingUtility; 038import org.apache.hadoop.hbase.TableName; 039import org.apache.hadoop.hbase.testclassification.ClientTests; 040import org.apache.hadoop.hbase.testclassification.MediumTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.junit.AfterClass; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Test; 046import org.junit.experimental.categories.Category; 047 048@Category({ MediumTests.class, ClientTests.class }) 049public class TestAsyncBufferMutator { 050 051 @ClassRule 052 public static final HBaseClassTestRule CLASS_RULE = 053 HBaseClassTestRule.forClass(TestAsyncBufferMutator.class); 054 055 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 056 057 private static TableName TABLE_NAME = TableName.valueOf("async"); 058 059 private static TableName MULTI_REGION_TABLE_NAME = TableName.valueOf("async-multi-region"); 060 061 private static byte[] CF = Bytes.toBytes("cf"); 062 063 private static byte[] CQ = Bytes.toBytes("cq"); 064 065 private static int COUNT = 100; 066 067 private static byte[] VALUE = new byte[1024]; 068 069 private static AsyncConnection CONN; 070 071 @BeforeClass 072 public static void setUp() throws Exception { 073 TEST_UTIL.startMiniCluster(1); 074 TEST_UTIL.createTable(TABLE_NAME, CF); 075 TEST_UTIL.createMultiRegionTable(MULTI_REGION_TABLE_NAME, CF); 076 CONN = ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration()).get(); 077 ThreadLocalRandom.current().nextBytes(VALUE); 078 } 079 080 @AfterClass 081 public static void tearDown() throws Exception { 082 CONN.close(); 083 TEST_UTIL.shutdownMiniCluster(); 084 } 085 086 @Test 087 public void testWithMultiRegionTable() throws InterruptedException { 088 test(MULTI_REGION_TABLE_NAME); 089 } 090 091 @Test 092 public void testWithSingleRegionTable() throws InterruptedException { 093 test(TABLE_NAME); 094 } 095 096 private void test(TableName tableName) throws InterruptedException { 097 List<CompletableFuture<Void>> futures = new ArrayList<>(); 098 try (AsyncBufferedMutator mutator = 099 CONN.getBufferedMutatorBuilder(tableName).setWriteBufferSize(16 * 1024).build()) { 100 List<CompletableFuture<Void>> fs = mutator.mutate(IntStream.range(0, COUNT / 2) 101 .mapToObj(i -> new Put(Bytes.toBytes(i)).addColumn(CF, CQ, VALUE)) 102 .collect(Collectors.toList())); 103 // exceeded the write buffer size, a flush will be called directly 104 fs.forEach(f -> f.join()); 105 IntStream.range(COUNT / 2, COUNT).forEach(i -> { 106 futures.add(mutator.mutate(new Put(Bytes.toBytes(i)).addColumn(CF, CQ, VALUE))); 107 }); 108 // the first future should have been sent out. 109 futures.get(0).join(); 110 Thread.sleep(2000); 111 // the last one should still be in write buffer 112 assertFalse(futures.get(futures.size() - 1).isDone()); 113 } 114 // mutator.close will call mutator.flush automatically so all tasks should have been done. 115 futures.forEach(f -> f.join()); 116 AsyncTable<?> table = CONN.getTable(tableName); 117 IntStream.range(0, COUNT).mapToObj(i -> new Get(Bytes.toBytes(i))).map(g -> table.get(g).join()) 118 .forEach(r -> { 119 assertArrayEquals(VALUE, r.getValue(CF, CQ)); 120 }); 121 } 122 123 @Test 124 public void testClosedMutate() throws InterruptedException { 125 AsyncBufferedMutator mutator = CONN.getBufferedMutator(TABLE_NAME); 126 mutator.close(); 127 Put put = new Put(Bytes.toBytes(0)).addColumn(CF, CQ, VALUE); 128 try { 129 mutator.mutate(put).get(); 130 fail("Close check failed"); 131 } catch (ExecutionException e) { 132 assertThat(e.getCause(), instanceOf(IOException.class)); 133 assertTrue(e.getCause().getMessage().startsWith("Already closed")); 134 } 135 for (CompletableFuture<Void> f : mutator.mutate(Arrays.asList(put))) { 136 try { 137 f.get(); 138 fail("Close check failed"); 139 } catch (ExecutionException e) { 140 assertThat(e.getCause(), instanceOf(IOException.class)); 141 assertTrue(e.getCause().getMessage().startsWith("Already closed")); 142 } 143 } 144 } 145}