/**
* Copyright 2009 The Apache Software Foundation
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.client.tableindexed;
import java.io.IOException;
import java.util.Random;
import junit.framework.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.HBaseClusterTestCase;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.PerformanceEvaluation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.RowLock;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.regionserver.tableindexed.IndexedRegionServer;
import org.apache.hadoop.hbase.util.Bytes;
public class TestIndexedTable extends HBaseClusterTestCase {
private static final Log LOG = LogFactory.getLog(TestIndexedTable.class);
private static final String TABLE_NAME = "table1";
private static final byte[] FAMILY_COLON = Bytes.toBytes("family:");
private static final byte[] FAMILY = Bytes.toBytes("family");
private static final byte[] QUAL_A = Bytes.toBytes("a");
private static final byte[] COL_A = Bytes.toBytes("family:a");
private static final String INDEX_COL_A = "A";
private static final int NUM_ROWS = 10;
private static final int MAX_VAL = 10000;
private IndexedTableAdmin admin;
private IndexedTable table;
private Random random = new Random();
private HTableDescriptor desc;
/** constructor */
public TestIndexedTable() {
conf
.set(HConstants.REGION_SERVER_IMPL, IndexedRegionServer.class.getName());
conf.setInt("hbase.master.info.port", -1);
conf.setInt("hbase.regionserver.info.port", -1);
}
@Override
protected void setUp() throws Exception {
super.setUp();
desc = new HTableDescriptor(TABLE_NAME);
desc.addFamily(new HColumnDescriptor(FAMILY_COLON));
IndexedTableDescriptor indexDesc = new IndexedTableDescriptor(desc);
// Create a new index that does lexicographic ordering on COL_A
IndexSpecification colAIndex = new IndexSpecification(INDEX_COL_A, COL_A);
indexDesc.addIndex(colAIndex);
admin = new IndexedTableAdmin(conf);
admin.createIndexedTable(indexDesc);
table = new IndexedTable(conf, desc.getName());
}
private void writeInitalRows() throws IOException {
for (int i = 0; i < NUM_ROWS; i++) {
Put update = new Put(PerformanceEvaluation.format(i));
byte[] valueA = PerformanceEvaluation.format(random.nextInt(MAX_VAL));
update.add(FAMILY, QUAL_A, valueA);
table.put(update);
LOG.info("Inserted row [" + Bytes.toString(update.getRow()) + "] val: ["
+ Bytes.toString(valueA) + "]");
}
}
public void testInitialWrites() throws IOException {
writeInitalRows();
assertRowsInOrder(NUM_ROWS);
}
private void assertRowsInOrder(int numRowsExpected)
throws IndexNotFoundException, IOException {
ResultScanner scanner = table.getIndexedScanner(INDEX_COL_A, null, null,
null, null, null);
int numRows = 0;
byte[] lastColA = null;
for (Result rowResult : scanner) {
byte[] colA = rowResult.getValue(COL_A);
LOG.info("index scan : row [" + Bytes.toString(rowResult.getRow())
+ "] value [" + Bytes.toString(colA) + "]");
if (lastColA != null) {
Assert.assertTrue(Bytes.compareTo(lastColA, colA) <= 0);
}
lastColA = colA;
numRows++;
}
scanner.close();
Assert.assertEquals(numRowsExpected, numRows);
}
private void assertRowUpdated(int updatedRow, int expectedRowValue)
throws IndexNotFoundException, IOException {
ResultScanner scanner = table.getIndexedScanner(INDEX_COL_A, null, null,
null, null, null);
byte[] persistedRowValue = null;
for (Result rowResult : scanner) {
byte[] row = rowResult.getRow();
byte[] value = rowResult.getValue(COL_A);
if (Bytes.toString(row).equals(Bytes.toString(PerformanceEvaluation.format(updatedRow)))) {
persistedRowValue = value;
LOG.info("update found: row [" + Bytes.toString(row)
+ "] value [" + Bytes.toString(value) + "]");
}
else
LOG.info("updated index scan : row [" + Bytes.toString(row)
+ "] value [" + Bytes.toString(value) + "]");
}
scanner.close();
Assert.assertEquals(Bytes.toString(PerformanceEvaluation.format(expectedRowValue)),
Bytes.toString(persistedRowValue));
}
private void assertRowDeleted(int numRowsExpected)
throws IndexNotFoundException, IOException {
// Check the size of the primary table
ResultScanner scanner = table.getScanner(new Scan());
int numRows = 0;
for (Result rowResult : scanner) {
byte[] colA = rowResult.getValue(FAMILY, QUAL_A);
LOG.info("primary scan : row [" + Bytes.toString(rowResult.getRow())
+ "] value [" + Bytes.toString(colA) + "]");
numRows++;
}
scanner.close();
Assert.assertEquals(numRowsExpected, numRows);
// Check the size of the index tables
assertRowsInOrder(numRowsExpected);
}
private void updateRow(int row, int newValue) throws IOException {
Put update = new Put(PerformanceEvaluation.format(row));
byte[] valueA = PerformanceEvaluation.format(newValue);
update.add(FAMILY, QUAL_A, valueA);
table.put(update);
LOG.info("Updated row [" + Bytes.toString(update.getRow()) + "] val: ["
+ Bytes.toString(valueA) + "]");
}
private void updateLockedRow(int row, int newValue) throws IOException {
RowLock lock = table.lockRow(PerformanceEvaluation.format(row));
Put update = new Put(PerformanceEvaluation.format(row), lock);
byte[] valueA = PerformanceEvaluation.format(newValue);
update.add(FAMILY, QUAL_A, valueA);
LOG.info("Updating row [" + Bytes.toString(update.getRow()) + "] val: ["
+ Bytes.toString(valueA) + "]");
table.put(update);
LOG.info("Updated row [" + Bytes.toString(update.getRow()) + "] val: ["
+ Bytes.toString(valueA) + "]");
table.unlockRow(lock);
}
private void updateLockedRowNoAutoFlush(int row, int newValue) throws IOException {
table.flushCommits();
table.setAutoFlush(false);
RowLock lock = table.lockRow(PerformanceEvaluation.format(row));
Put update = new Put(PerformanceEvaluation.format(row), lock);
byte[] valueA = PerformanceEvaluation.format(newValue);
update.add(FAMILY, QUAL_A, valueA);
LOG.info("Updating row [" + Bytes.toString(update.getRow()) + "] val: ["
+ Bytes.toString(valueA) + "]");
table.put(update);
LOG.info("Updated row [" + Bytes.toString(update.getRow()) + "] val: ["
+ Bytes.toString(valueA) + "]");
table.flushCommits();
table.close();
table = new IndexedTable(conf, desc.getName());
}
public void testMultipleWrites() throws IOException {
writeInitalRows();
writeInitalRows(); // Update the rows.
assertRowsInOrder(NUM_ROWS);
}
public void testDelete() throws IOException {
writeInitalRows();
// Delete the first row;
table.deleteAll(PerformanceEvaluation.format(0));
assertRowsInOrder(NUM_ROWS - 1);
}
public void testRowUpdate() throws IOException {
writeInitalRows();
int row = NUM_ROWS - 2;
int value = MAX_VAL + 111;
updateRow(row, value);
assertRowUpdated(row, value);
}
public void testLockedRowUpdate() throws IOException {
writeInitalRows();
int row = NUM_ROWS - 2;
int value = MAX_VAL + 111;
updateLockedRow(row, value);
assertRowUpdated(row, value);
}
public void testLockedRowUpdateNoAutoFlush() throws IOException {
writeInitalRows();
int row = NUM_ROWS - 4;
int value = MAX_VAL + 2222;
updateLockedRowNoAutoFlush(row, value);
assertRowUpdated(row, value);
}
public void testLockedRowDelete() throws IOException {
writeInitalRows();
// Delete the first row;
byte[] row = PerformanceEvaluation.format(0);
RowLock lock = table.lockRow(row);
table.delete(new Delete(row, HConstants.LATEST_TIMESTAMP, lock));
table.unlockRow(lock);
assertRowDeleted(NUM_ROWS - 1);
}
}