blob: 970526ee6cafbf6f975f094de267440bf6e819fc [file] [log] [blame]
/*
* Copyright 2009-2010 by The Regents of the University of California
* Licensed 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 from
*
* 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 edu.uci.ics.hyracks.storage.am.btree;
import static org.junit.Assert.fail;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Test;
import edu.uci.ics.hyracks.api.dataflow.value.IBinaryComparatorFactory;
import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
import edu.uci.ics.hyracks.api.dataflow.value.ITypeTraits;
import edu.uci.ics.hyracks.data.std.accessors.PointableBinaryComparatorFactory;
import edu.uci.ics.hyracks.data.std.primitive.IntegerPointable;
import edu.uci.ics.hyracks.data.std.primitive.UTF8StringPointable;
import edu.uci.ics.hyracks.dataflow.common.comm.io.ArrayTupleBuilder;
import edu.uci.ics.hyracks.dataflow.common.comm.io.ArrayTupleReference;
import edu.uci.ics.hyracks.dataflow.common.data.accessors.ITupleReference;
import edu.uci.ics.hyracks.dataflow.common.data.marshalling.IntegerSerializerDeserializer;
import edu.uci.ics.hyracks.dataflow.common.data.marshalling.UTF8StringSerializerDeserializer;
import edu.uci.ics.hyracks.dataflow.common.util.TupleUtils;
import edu.uci.ics.hyracks.storage.am.btree.impls.RangePredicate;
import edu.uci.ics.hyracks.storage.am.btree.util.BTreeUtils;
import edu.uci.ics.hyracks.storage.am.common.TestOperationCallback;
import edu.uci.ics.hyracks.storage.am.common.api.IIndexAccessor;
import edu.uci.ics.hyracks.storage.am.common.api.IIndexBulkLoader;
import edu.uci.ics.hyracks.storage.am.common.api.IIndexCursor;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndex;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexAccessor;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexCursor;
import edu.uci.ics.hyracks.storage.am.common.api.TreeIndexException;
import edu.uci.ics.hyracks.storage.am.common.api.UnsortedInputException;
import edu.uci.ics.hyracks.storage.am.common.impls.TreeIndexDiskOrderScanCursor;
import edu.uci.ics.hyracks.storage.am.common.ophelpers.MultiComparator;
@SuppressWarnings("rawtypes")
public abstract class OrderedIndexExamplesTest {
protected static final Logger LOGGER = Logger.getLogger(OrderedIndexExamplesTest.class.getName());
protected final Random rnd = new Random(50);
protected abstract ITreeIndex createTreeIndex(ITypeTraits[] typeTraits, IBinaryComparatorFactory[] cmpFactories,
int[] bloomFilterKeyFields) throws TreeIndexException;
/**
* Fixed-Length Key,Value Example. Create a tree index with one fixed-length
* key field and one fixed-length value field. Fill index with random values
* using insertions (not bulk load). Perform scans and range search.
*/
@Test
public void fixedLengthKeyValueExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Fixed-Length Key,Value Example.");
}
// Declare fields.
int fieldCount = 2;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = IntegerPointable.TYPE_TRAITS;
typeTraits[1] = IntegerPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { IntegerSerializerDeserializer.INSTANCE,
IntegerSerializerDeserializer.INSTANCE };
// Declare keys.
int keyFieldCount = 1;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(IntegerPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
long start = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Inserting into tree...");
}
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
int numInserts = 10000;
for (int i = 0; i < numInserts; i++) {
int f0 = rnd.nextInt() % numInserts;
int f1 = 5;
TupleUtils.createIntegerTuple(tb, tuple, f0, f1);
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Inserting " + i + " : " + f0 + " " + f1);
}
}
try {
indexAccessor.insert(tuple);
} catch (TreeIndexException e) {
}
}
long end = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(numInserts + " inserts in " + (end - start) + "ms");
}
orderedScan(indexAccessor, fieldSerdes);
diskOrderScan(indexAccessor, fieldSerdes);
// Build low key.
ArrayTupleBuilder lowKeyTb = new ArrayTupleBuilder(keyFieldCount);
ArrayTupleReference lowKey = new ArrayTupleReference();
TupleUtils.createIntegerTuple(lowKeyTb, lowKey, -1000);
// Build high key.
ArrayTupleBuilder highKeyTb = new ArrayTupleBuilder(keyFieldCount);
ArrayTupleReference highKey = new ArrayTupleReference();
TupleUtils.createIntegerTuple(highKeyTb, highKey, 1000);
rangeSearch(cmpFactories, indexAccessor, fieldSerdes, lowKey, highKey);
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* This test the btree page split. Originally this test didn't pass since
* the btree was spliting by cardinality and not size. Thus, we might end
* up with a situation where there is not enough space to insert the new
* tuple after the split which will throw an error and the split won't be
* propagated to upper level; thus, the tree is corrupted. Now, it split
* page by size. The correct behavior on abnormally large keys/values.
*/
@Test
public void pageSplitTestExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("BTree page split test.");
}
// Declare fields.
int fieldCount = 2;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = UTF8StringPointable.TYPE_TRAITS;
typeTraits[1] = UTF8StringPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { UTF8StringSerializerDeserializer.INSTANCE,
UTF8StringSerializerDeserializer.INSTANCE };
// Declare keys.
int keyFieldCount = 1;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(UTF8StringPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
String key = "111";
String data = "XXX";
TupleUtils.createTuple(tb, tuple, fieldSerdes, key, data);
indexAccessor.insert(tuple);
key = "222";
data = "XXX";
TupleUtils.createTuple(tb, tuple, fieldSerdes, key, data);
indexAccessor.insert(tuple);
key = "333";
data = "XXX";
TupleUtils.createTuple(tb, tuple, fieldSerdes, key, data);
indexAccessor.insert(tuple);
key = "444";
data = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
TupleUtils.createTuple(tb, tuple, fieldSerdes, key, data);
indexAccessor.insert(tuple);
key = "555";
data = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
TupleUtils.createTuple(tb, tuple, fieldSerdes, key, data);
indexAccessor.insert(tuple);
key = "666";
data = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
TupleUtils.createTuple(tb, tuple, fieldSerdes, key, data);
indexAccessor.insert(tuple);
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* Composite Key Example (Non-Unique Index). Create a tree index with two
* fixed-length key fields and one fixed-length value field. Fill index with
* random values using insertions (not bulk load) Perform scans and range
* search.
*/
@Test
public void twoFixedLengthKeysOneFixedLengthValueExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Composite Key Test");
}
// Declare fields.
int fieldCount = 3;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = IntegerPointable.TYPE_TRAITS;
typeTraits[1] = IntegerPointable.TYPE_TRAITS;
typeTraits[2] = IntegerPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { IntegerSerializerDeserializer.INSTANCE,
IntegerSerializerDeserializer.INSTANCE, IntegerSerializerDeserializer.INSTANCE };
// declare keys
int keyFieldCount = 2;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(IntegerPointable.FACTORY);
cmpFactories[1] = PointableBinaryComparatorFactory.of(IntegerPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
bloomFilterKeyFields[1] = 1;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
long start = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Inserting into tree...");
}
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
int numInserts = 10000;
for (int i = 0; i < 10000; i++) {
int f0 = rnd.nextInt() % 2000;
int f1 = rnd.nextInt() % 1000;
int f2 = 5;
TupleUtils.createIntegerTuple(tb, tuple, f0, f1, f2);
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Inserting " + i + " : " + f0 + " " + f1 + " " + f2);
}
}
try {
indexAccessor.insert(tuple);
} catch (TreeIndexException e) {
}
}
long end = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(numInserts + " inserts in " + (end - start) + "ms");
}
orderedScan(indexAccessor, fieldSerdes);
diskOrderScan(indexAccessor, fieldSerdes);
// Build low key.
ArrayTupleBuilder lowKeyTb = new ArrayTupleBuilder(1);
ArrayTupleReference lowKey = new ArrayTupleReference();
TupleUtils.createIntegerTuple(lowKeyTb, lowKey, -3);
// Build high key.
ArrayTupleBuilder highKeyTb = new ArrayTupleBuilder(1);
ArrayTupleReference highKey = new ArrayTupleReference();
TupleUtils.createIntegerTuple(highKeyTb, highKey, 3);
// Prefix-Range search in [-3, 3]
rangeSearch(cmpFactories, indexAccessor, fieldSerdes, lowKey, highKey);
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* Variable-Length Example. Create a BTree with one variable-length key
* field and one variable-length value field. Fill BTree with random values
* using insertions (not bulk load) Perform ordered scans and range search.
*/
@Test
public void varLenKeyValueExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Variable-Length Key,Value Example");
}
// Declare fields.
int fieldCount = 2;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = UTF8StringPointable.TYPE_TRAITS;
typeTraits[1] = UTF8StringPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { UTF8StringSerializerDeserializer.INSTANCE,
UTF8StringSerializerDeserializer.INSTANCE };
// Declare keys.
int keyFieldCount = 1;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(UTF8StringPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
long start = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Inserting into tree...");
}
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
// Max string length to be generated.
int maxLength = 10;
int numInserts = 10000;
for (int i = 0; i < 10000; i++) {
String f0 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
String f1 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
TupleUtils.createTuple(tb, tuple, fieldSerdes, f0, f1);
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Inserting[" + i + "] " + f0 + " " + f1);
}
}
try {
indexAccessor.insert(tuple);
} catch (TreeIndexException e) {
}
}
long end = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(numInserts + " inserts in " + (end - start) + "ms");
}
orderedScan(indexAccessor, fieldSerdes);
diskOrderScan(indexAccessor, fieldSerdes);
// Build low key.
ArrayTupleBuilder lowKeyTb = new ArrayTupleBuilder(1);
ArrayTupleReference lowKey = new ArrayTupleReference();
TupleUtils.createTuple(lowKeyTb, lowKey, fieldSerdes, "cbf");
// Build high key.
ArrayTupleBuilder highKeyTb = new ArrayTupleBuilder(1);
ArrayTupleReference highKey = new ArrayTupleReference();
TupleUtils.createTuple(highKeyTb, highKey, fieldSerdes, "cc7");
rangeSearch(cmpFactories, indexAccessor, fieldSerdes, lowKey, highKey);
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* Deletion Example. Create a BTree with one variable-length key field and
* one variable-length value field. Fill B-tree with random values using
* insertions, then delete entries one-by-one. Repeat procedure a few times
* on same BTree.
*/
@Test
public void deleteExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Deletion Example");
}
// Declare fields.
int fieldCount = 2;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = UTF8StringPointable.TYPE_TRAITS;
typeTraits[1] = UTF8StringPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { UTF8StringSerializerDeserializer.INSTANCE,
UTF8StringSerializerDeserializer.INSTANCE };
// Declare keys.
int keyFieldCount = 1;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(UTF8StringPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
// Max string length to be generated.
int runs = 3;
for (int run = 0; run < runs; run++) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Deletion example run: " + (run + 1) + "/" + runs);
LOGGER.info("Inserting into tree...");
}
int maxLength = 10;
int ins = 10000;
String[] f0s = new String[ins];
String[] f1s = new String[ins];
int insDone = 0;
int[] insDoneCmp = new int[ins];
for (int i = 0; i < ins; i++) {
String f0 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
String f1 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
TupleUtils.createTuple(tb, tuple, fieldSerdes, f0, f1);
f0s[i] = f0;
f1s[i] = f1;
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Inserting " + i);
}
}
try {
indexAccessor.insert(tuple);
insDone++;
} catch (TreeIndexException e) {
}
insDoneCmp[i] = insDone;
}
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Deleting from tree...");
}
int delDone = 0;
for (int i = 0; i < ins; i++) {
TupleUtils.createTuple(tb, tuple, fieldSerdes, f0s[i], f1s[i]);
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Deleting " + i);
}
}
try {
indexAccessor.delete(tuple);
delDone++;
} catch (TreeIndexException e) {
}
if (insDoneCmp[i] != delDone) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("INCONSISTENT STATE, ERROR IN DELETION EXAMPLE.");
LOGGER.info("INSDONECMP: " + insDoneCmp[i] + " " + delDone);
}
break;
}
}
if (insDone != delDone) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("ERROR! INSDONE: " + insDone + " DELDONE: " + delDone);
}
break;
}
}
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* Update example. Create a BTree with one variable-length key field and one
* variable-length value field. Fill B-tree with random values using
* insertions, then update entries one-by-one. Repeat procedure a few times
* on same BTree.
*/
@Test
public void updateExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Update example");
}
// Declare fields.
int fieldCount = 2;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = UTF8StringPointable.TYPE_TRAITS;
typeTraits[1] = UTF8StringPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { UTF8StringSerializerDeserializer.INSTANCE,
UTF8StringSerializerDeserializer.INSTANCE };
// Declare keys.
int keyFieldCount = 1;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(UTF8StringPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Inserting into tree...");
}
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
int maxLength = 10;
int ins = 10000;
String[] keys = new String[10000];
for (int i = 0; i < ins; i++) {
String f0 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
String f1 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
TupleUtils.createTuple(tb, tuple, fieldSerdes, f0, f1);
keys[i] = f0;
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Inserting " + i);
}
}
try {
indexAccessor.insert(tuple);
} catch (TreeIndexException e) {
}
}
// Print before doing any updates.
orderedScan(indexAccessor, fieldSerdes);
int runs = 3;
for (int run = 0; run < runs; run++) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Update test run: " + (run + 1) + "/" + runs);
LOGGER.info("Updating BTree");
}
for (int i = 0; i < ins; i++) {
// Generate a new random value for f1.
String f1 = randomString(Math.abs(rnd.nextInt()) % maxLength + 1, rnd);
TupleUtils.createTuple(tb, tuple, fieldSerdes, keys[i], f1);
if (LOGGER.isLoggable(Level.INFO)) {
if (i % 1000 == 0) {
LOGGER.info("Updating " + i);
}
}
try {
indexAccessor.update(tuple);
} catch (TreeIndexException e) {
} catch (UnsupportedOperationException e) {
}
}
// Do another scan after a round of updates.
orderedScan(indexAccessor, fieldSerdes);
}
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* Bulk load example. Load a tree with 100,000 tuples. BTree has a composite
* key to "simulate" non-unique index creation.
*/
@Test
public void bulkLoadExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Bulk load example");
}
// Declare fields.
int fieldCount = 3;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = IntegerPointable.TYPE_TRAITS;
typeTraits[1] = IntegerPointable.TYPE_TRAITS;
typeTraits[2] = IntegerPointable.TYPE_TRAITS;
// Declare field serdes.
ISerializerDeserializer[] fieldSerdes = { IntegerSerializerDeserializer.INSTANCE,
IntegerSerializerDeserializer.INSTANCE, IntegerSerializerDeserializer.INSTANCE };
// declare keys
int keyFieldCount = 2;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(IntegerPointable.FACTORY);
cmpFactories[1] = PointableBinaryComparatorFactory.of(IntegerPointable.FACTORY);
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
bloomFilterKeyFields[1] = 1;
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
// Load sorted records.
int ins = 100000;
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Bulk loading " + ins + " tuples");
}
long start = System.currentTimeMillis();
IIndexBulkLoader bulkLoader = treeIndex.createBulkLoader(0.7f, false, ins);
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
for (int i = 0; i < ins; i++) {
TupleUtils.createIntegerTuple(tb, tuple, i, i, 5);
bulkLoader.add(tuple);
}
bulkLoader.end();
long end = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(ins + " tuples loaded in " + (end - start) + "ms");
}
IIndexAccessor indexAccessor = (IIndexAccessor) treeIndex.createAccessor(TestOperationCallback.INSTANCE,
TestOperationCallback.INSTANCE);
// Build low key.
ArrayTupleBuilder lowKeyTb = new ArrayTupleBuilder(1);
ArrayTupleReference lowKey = new ArrayTupleReference();
TupleUtils.createIntegerTuple(lowKeyTb, lowKey, 44444);
// Build high key.
ArrayTupleBuilder highKeyTb = new ArrayTupleBuilder(1);
ArrayTupleReference highKey = new ArrayTupleReference();
TupleUtils.createIntegerTuple(highKeyTb, highKey, 44500);
// Prefix-Range search in [44444, 44500]
rangeSearch(cmpFactories, indexAccessor, fieldSerdes, lowKey, highKey);
treeIndex.validate();
treeIndex.deactivate();
treeIndex.destroy();
}
/**
* Bulk load failure example. Repeatedly loads a tree with 1,000 tuples, of
* which one tuple at each possible position does not conform to the
* expected order. We expect the bulk load to fail with an exception.
*/
@Test
public void bulkOrderVerificationExample() throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Bulk load order verification example");
}
// Declare fields.
int fieldCount = 2;
ITypeTraits[] typeTraits = new ITypeTraits[fieldCount];
typeTraits[0] = IntegerPointable.TYPE_TRAITS;
typeTraits[1] = IntegerPointable.TYPE_TRAITS;
// declare keys
int keyFieldCount = 1;
IBinaryComparatorFactory[] cmpFactories = new IBinaryComparatorFactory[keyFieldCount];
cmpFactories[0] = PointableBinaryComparatorFactory.of(IntegerPointable.FACTORY);
Random rnd = new Random();
ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
ArrayTupleReference tuple = new ArrayTupleReference();
// This is only used for the LSM-BTree.
int[] bloomFilterKeyFields = new int[keyFieldCount];
bloomFilterKeyFields[0] = 0;
int ins = 1000;
for (int i = 1; i < ins; i++) {
ITreeIndex treeIndex = createTreeIndex(typeTraits, cmpFactories, bloomFilterKeyFields);
treeIndex.create();
treeIndex.activate();
// Load sorted records, and expect to fail at tuple i.
IIndexBulkLoader bulkLoader = treeIndex.createBulkLoader(0.7f, true, ins);
for (int j = 0; j < ins; j++) {
if (j > i) {
fail("Bulk load failure test unexpectedly succeeded past tuple: " + j);
}
int key = j;
if (j == i) {
int swapElementCase = Math.abs(rnd.nextInt()) % 2;
if (swapElementCase == 0) {
// Element equal to previous element.
key--;
} else {
// Element smaller than previous element.
key -= Math.abs(Math.random() % (ins - 1)) + 1;
}
}
TupleUtils.createIntegerTuple(tb, tuple, key, 5);
try {
bulkLoader.add(tuple);
} catch (UnsortedInputException e) {
if (j != i) {
fail("Unexpected exception: " + e.getMessage());
}
// Success.
break;
}
}
treeIndex.deactivate();
treeIndex.destroy();
}
}
private void orderedScan(IIndexAccessor indexAccessor, ISerializerDeserializer[] fieldSerdes) throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Ordered Scan:");
}
IIndexCursor scanCursor = (IIndexCursor) indexAccessor.createSearchCursor();
RangePredicate nullPred = new RangePredicate(null, null, true, true, null, null);
indexAccessor.search(scanCursor, nullPred);
try {
while (scanCursor.hasNext()) {
scanCursor.next();
ITupleReference frameTuple = scanCursor.getTuple();
String rec = TupleUtils.printTuple(frameTuple, fieldSerdes);
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(rec);
}
}
} finally {
scanCursor.close();
}
}
private void diskOrderScan(IIndexAccessor indexAccessor, ISerializerDeserializer[] fieldSerdes) throws Exception {
try {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Disk-Order Scan:");
}
ITreeIndexAccessor treeIndexAccessor = (ITreeIndexAccessor) indexAccessor;
TreeIndexDiskOrderScanCursor diskOrderCursor = (TreeIndexDiskOrderScanCursor) treeIndexAccessor
.createDiskOrderScanCursor();
treeIndexAccessor.diskOrderScan(diskOrderCursor);
try {
while (diskOrderCursor.hasNext()) {
diskOrderCursor.next();
ITupleReference frameTuple = diskOrderCursor.getTuple();
String rec = TupleUtils.printTuple(frameTuple, fieldSerdes);
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(rec);
}
}
} finally {
diskOrderCursor.close();
}
} catch (UnsupportedOperationException e) {
// Ignore exception because some indexes, e.g. the LSMBTree, don't
// support disk-order scan.
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Ignoring disk-order scan since it's not supported.");
}
} catch (ClassCastException e) {
// Ignore exception because IIndexAccessor sometimes isn't
// an ITreeIndexAccessor, e.g., for the LSMBTree.
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Ignoring disk-order scan since it's not supported.");
}
}
}
private void rangeSearch(IBinaryComparatorFactory[] cmpFactories, IIndexAccessor indexAccessor,
ISerializerDeserializer[] fieldSerdes, ITupleReference lowKey, ITupleReference highKey) throws Exception {
if (LOGGER.isLoggable(Level.INFO)) {
String lowKeyString = TupleUtils.printTuple(lowKey, fieldSerdes);
String highKeyString = TupleUtils.printTuple(highKey, fieldSerdes);
LOGGER.info("Range-Search in: [ " + lowKeyString + ", " + highKeyString + "]");
}
ITreeIndexCursor rangeCursor = (ITreeIndexCursor) indexAccessor.createSearchCursor();
MultiComparator lowKeySearchCmp = BTreeUtils.getSearchMultiComparator(cmpFactories, lowKey);
MultiComparator highKeySearchCmp = BTreeUtils.getSearchMultiComparator(cmpFactories, highKey);
RangePredicate rangePred = new RangePredicate(lowKey, highKey, true, true, lowKeySearchCmp, highKeySearchCmp);
indexAccessor.search(rangeCursor, rangePred);
try {
while (rangeCursor.hasNext()) {
rangeCursor.next();
ITupleReference frameTuple = rangeCursor.getTuple();
String rec = TupleUtils.printTuple(frameTuple, fieldSerdes);
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(rec);
}
}
} finally {
rangeCursor.close();
}
}
public static String randomString(int length, Random random) {
String s = Long.toHexString(Double.doubleToLongBits(random.nextDouble()));
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < s.length() && i < length; i++) {
strBuilder.append(s.charAt(Math.abs(random.nextInt()) % s.length()));
}
return strBuilder.toString();
}
}