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 */ 018 019package org.apache.hadoop.hbase.util; 020 021import static org.apache.hadoop.hbase.util.test.LoadTestDataGenerator.INCREMENT; 022import static org.apache.hadoop.hbase.util.test.LoadTestDataGenerator.MUTATE_INFO; 023 024import java.io.IOException; 025import java.io.PrintWriter; 026import java.io.StringWriter; 027import java.util.Arrays; 028import java.util.HashSet; 029import java.util.Set; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.apache.hadoop.conf.Configuration; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.HTableInterface; 037import org.apache.hadoop.hbase.client.Put; 038import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException; 039import org.apache.hadoop.hbase.client.Table; 040import org.apache.hadoop.hbase.util.test.LoadTestDataGenerator; 041import org.apache.hadoop.util.StringUtils; 042 043/** Creates multiple threads that write key/values into the */ 044public class MultiThreadedWriter extends MultiThreadedWriterBase { 045 private static final Log LOG = LogFactory.getLog(MultiThreadedWriter.class); 046 047 protected Set<HBaseWriterThread> writers = new HashSet<HBaseWriterThread>(); 048 049 protected boolean isMultiPut = false; 050 051 public MultiThreadedWriter(LoadTestDataGenerator dataGen, Configuration conf, 052 TableName tableName) throws IOException { 053 super(dataGen, conf, tableName, "W"); 054 } 055 056 /** Use multi-puts vs. separate puts for every column in a row */ 057 public void setMultiPut(boolean isMultiPut) { 058 this.isMultiPut = isMultiPut; 059 } 060 061 @Override 062 public void start(long startKey, long endKey, int numThreads) throws IOException { 063 super.start(startKey, endKey, numThreads); 064 065 if (verbose) { 066 LOG.debug("Inserting keys [" + startKey + ", " + endKey + ")"); 067 } 068 069 createWriterThreads(numThreads); 070 071 startThreads(writers); 072 } 073 074 protected void createWriterThreads(int numThreads) throws IOException { 075 for (int i = 0; i < numThreads; ++i) { 076 HBaseWriterThread writer = new HBaseWriterThread(i); 077 Threads.setLoggingUncaughtExceptionHandler(writer); 078 writers.add(writer); 079 } 080 } 081 082 public class HBaseWriterThread extends Thread { 083 private final Table table; 084 085 public HBaseWriterThread(int writerId) throws IOException { 086 setName(getClass().getSimpleName() + "_" + writerId); 087 table = createTable(); 088 } 089 090 protected HTableInterface createTable() throws IOException { 091 return connection.getTable(tableName); 092 } 093 094 @Override 095 public void run() { 096 try { 097 long rowKeyBase; 098 byte[][] columnFamilies = dataGenerator.getColumnFamilies(); 099 while ((rowKeyBase = nextKeyToWrite.getAndIncrement()) < endKey) { 100 byte[] rowKey = dataGenerator.getDeterministicUniqueKey(rowKeyBase); 101 Put put = new Put(rowKey); 102 numKeys.addAndGet(1); 103 int columnCount = 0; 104 for (byte[] cf : columnFamilies) { 105 byte[][] columns = dataGenerator.generateColumnsForCf(rowKey, cf); 106 for (byte[] column : columns) { 107 byte[] value = dataGenerator.generateValue(rowKey, cf, column); 108 put.addColumn(cf, column, value); 109 ++columnCount; 110 if (!isMultiPut) { 111 insert(table, put, rowKeyBase); 112 numCols.addAndGet(1); 113 put = new Put(rowKey); 114 } 115 } 116 long rowKeyHash = Arrays.hashCode(rowKey); 117 put.addColumn(cf, MUTATE_INFO, HConstants.EMPTY_BYTE_ARRAY); 118 put.addColumn(cf, INCREMENT, Bytes.toBytes(rowKeyHash)); 119 if (!isMultiPut) { 120 insert(table, put, rowKeyBase); 121 numCols.addAndGet(1); 122 put = new Put(rowKey); 123 } 124 } 125 if (isMultiPut) { 126 if (verbose) { 127 LOG.debug("Preparing put for key = [" + rowKey + "], " + columnCount + " columns"); 128 } 129 insert(table, put, rowKeyBase); 130 numCols.addAndGet(columnCount); 131 } 132 if (trackWroteKeys) { 133 wroteKeys.add(rowKeyBase); 134 } 135 } 136 } finally { 137 closeHTable(); 138 numThreadsWorking.decrementAndGet(); 139 } 140 } 141 142 public void insert(Table table, Put put, long keyBase) { 143 long start = System.currentTimeMillis(); 144 try { 145 put = (Put) dataGenerator.beforeMutate(keyBase, put); 146 table.put(put); 147 totalOpTimeMs.addAndGet(System.currentTimeMillis() - start); 148 } catch (IOException e) { 149 failedKeySet.add(keyBase); 150 String exceptionInfo; 151 if (e instanceof RetriesExhaustedWithDetailsException) { 152 RetriesExhaustedWithDetailsException aggEx = (RetriesExhaustedWithDetailsException)e; 153 exceptionInfo = aggEx.getExhaustiveDescription(); 154 } else { 155 StringWriter stackWriter = new StringWriter(); 156 PrintWriter pw = new PrintWriter(stackWriter); 157 e.printStackTrace(pw); 158 pw.flush(); 159 exceptionInfo = StringUtils.stringifyException(e); 160 } 161 LOG.error("Failed to insert: " + keyBase + " after " + (System.currentTimeMillis() - start) 162 + "ms; region information: " + getRegionDebugInfoSafe(table, put.getRow()) 163 + "; errors: " + exceptionInfo); 164 } 165 } 166 protected void closeHTable() { 167 try { 168 if (table != null) { 169 table.close(); 170 } 171 } catch (IOException e) { 172 LOG.error("Error closing table", e); 173 } 174 } 175 } 176 177 @Override 178 public void waitForFinish() { 179 super.waitForFinish(); 180 System.out.println("Failed to write keys: " + failedKeySet.size()); 181 for (Long key : failedKeySet) { 182 System.out.println("Failed to write key: " + key); 183 } 184 } 185}