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}