![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.btree.DumpIndexSegment Maven / Gradle / Ivy
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on May 15, 2008
*/
package com.bigdata.btree;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.bigdata.btree.IndexSegment.ImmutableNodeFactory.ImmutableLeaf;
import com.bigdata.io.DirectBufferPool;
import com.bigdata.rawstore.IRawStore;
import com.bigdata.util.InnerCause;
/**
* Utility to examine the context of an {@link IndexSegmentStore}.
*
* @author Bryan Thompson
* @version $Id$
*/
public class DumpIndexSegment {
protected static Logger log = Logger.getLogger(DumpIndexSegment.class);
public static void usage() {
System.err.println("usage: " + DumpIndexSegment.class.getSimpleName()
+ " [options] " + " file(s)");
// @todo declare the options that the class understands.
System.err.println("options:");
System.err.println(" -d level: set the logger level");
}
/**
* Dump one or more {@link IndexSegment}s.
*
* Note: The -nodeState
and -leafState
options
* also require you to turn up the logging level in order to see the output.
* However, when true they will apply a variety of validation tests to the
* nodes and leaves regardless of whether their state is written onto the
* console.
*
* @param args
* usage [file|-d level|-nodeState|-leafState]+
,
* where
*
* - file
*
* is the name of a n {@link IndexSegmentStore} file
*
* - level
*
* is the name of the {@link Level} to be used for the
* {@link AbstractBTree#dumpLog}
*
* - -nodeState
*
* Enables the dump of the {@link Node} state
*
* - -leafState
*
* Enables the dump of the {@link Node} state
*
*
*
* @throws IOException
*/
public static void main(final String[] args) throws IOException {
if (args.length == 0) {
usage();
System.exit(1);
}
boolean dumpNodeState = false;
boolean dumpLeafState = false;
for (int i = 0; i < args.length; i++) {
final String arg = args[i];
if (arg.startsWith("-")) {
if (arg.equals("-d")) {
final Level level = Level.toLevel(args[++i]);
System.out.println("Setting log level: "+level);
// turn up the dumpLog level so that we can see the output.
try {
AbstractBTree.dumpLog.setLevel(level);
} catch (Throwable t) {
/*
* Note: The SLF4J logging bridge can cause a
* NoSuchMethodException to be thrown here.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/362
*/
if (InnerCause.isInnerCause(t,
NoSuchMethodException.class)) {
log.error("Could not set log level : "
+ AbstractBTree.dumpLog.getName());
} else {
// Some other problem.
throw new RuntimeException(t);
}
}
} else if(arg.equals("-nodeState")) {
dumpNodeState = true;
} else if(arg.equals("-leafState")) {
dumpLeafState = true;
} else {
System.err.println("Unknown option: "+arg);
System.exit( 1 );
}
} else {
final File file = new File(arg);
if (!file.exists()) {
System.err.println("No such file: " + file);
continue;
}
dumpIndexSegment(file, dumpNodeState, dumpLeafState);
}
}
}
static void dumpIndexSegment(final File file, final boolean dumpNodeState,
final boolean dumpLeafState) throws IOException {
final IndexSegmentStore store = new IndexSegmentStore(file);
// dump the checkpoint record, index metadata record, etc.
dumpHeaders(store);
final AbstractNode> root = store.loadIndexSegment().getRoot();
// dump the node state.
if (root instanceof Node) {
writeBanner("dump nodes");
// dump the nodes (not the leaves).
dumpNodes(store, (Node) root, dumpNodeState);
}
// multi-block scan of the index segment.
boolean multiBlockScan = false; // @todo command line option.
if (multiBlockScan) {
writeBanner("dump leaves using multi-block forward scan");
dumpLeavesMultiBlockForwardScan(store);
}
// dump the leaves using a fast reverse scan.
boolean fastReverseScan = true;// @todo command line option
if (fastReverseScan) {
writeBanner("dump leaves using fast reverse scan");
dumpLeavesReverseScan(store, dumpLeafState);
}
// dump the leaves using a fast forward scan.
boolean fastForwardScan = true;// @todo command line option
if (fastForwardScan) {
writeBanner("dump leaves using fast forward scan");
dumpLeavesForwardScan(store, dumpLeafState);
}
// dump the index contents
boolean entryScan = true;// @todo command line option.
boolean showTuples = false;// @todo command line option.
if (entryScan) {
writeBanner("dump keys and values using iterator");
DumpIndex.dumpIndex(store.loadIndexSegment(),showTuples);
}
}
static void dumpHeaders(final IndexSegmentStore store) throws IOException {
System.out.println("file : " + store.getFile());
System.out.println("checkpoint : " + store.getCheckpoint().toString());
System.out.println("metadata : " + store.getIndexMetadata().toString());
System.out.println("bloomFilter : "
+ (store.getCheckpoint().addrBloom != IRawStore.NULL ? store
.readBloomFilter().toString() : "N/A"));
}
/**
* Dumps nodes (but not leaves) using a low-level approach.
*
* @param store
*
* @param node
*/
static void dumpNodes(final IndexSegmentStore store, final Node node,
final boolean dumpNodeState) {
if(dumpNodeState)
node.dump(System.out);
final int nkeys = node.getKeyCount();
for (int i = 0; i <= nkeys; i++) {
final long addr = node.getChildAddr(i);
if (store.getAddressManager().isNodeAddr(addr)) {
// normal read following the node hierarchy, using cache, etc.
final Node child = (Node) node.getChild(i);
// recursive dump
dumpNodes(store, child, dumpNodeState);
}
}
}
/**
* Low-level routine descends the left-most path from the root and returns
* the address of the left-most leaf.
*
* @param store
* @param addr
* @return
*/
static long getFirstLeafAddr(final IndexSegmentStore store, final long addr) {
if(store.getAddressManager().isNodeAddr(addr)) {
// lower level read
final ByteBuffer data = store.read(addr);
final AbstractBTree btree = store.loadIndexSegment();
// note: does NOT set the parent reference on the read Node!
final Node child = (Node) decode(btree, addr, data);
// left most child
return getFirstLeafAddr(store, child.getChildAddr(0));
}
// found the left most leaf.
return addr;
}
/**
* Convenience method used by {@link DumpIndexSegment} does not track the
* decode time, etc. It also does not set the parent reference on the
* node/leaf.
*
* @param btree
* The owning B+Tree.
* @param addr
* The address of the data record in the backing store.
* @param buf
* The data record.
*
* @return The node or leaf.
*/
static AbstractNode> decode(final AbstractBTree btree, final long addr,
final ByteBuffer buf) {
return btree.nodeSer.wrap(btree, addr, btree.nodeSer.decode(buf));
}
/**
* Low-level routine descends the left-most path from the root and returns
* the address of the left-most leaf.
*
* @param store
* @param addr
* @return
*/
static long getLastLeafAddr(final IndexSegmentStore store, final long addr) {
if(store.getAddressManager().isNodeAddr(addr)) {
// lower level read
final ByteBuffer data = store.read(addr);
final AbstractBTree btree = store.loadIndexSegment();
// note: does NOT set the parent reference on the read Node!
final Node child = (Node) decode(btree, addr,
data);
// right most child
return getLastLeafAddr(store, child.getChildAddr(child.getKeyCount()));
}
// found the right most leaf.
return addr;
}
/**
* Dump leaves by direct record scan from first leaf offset until end of
* leaves region.
*
* Note: While this could be rewritten for cleaner code to use
* {@link IndexSegment#leafIterator(boolean)} but it would make it harder to
* spot problems in the data.
*
* @param store
*/
static void dumpLeavesReverseScan(final IndexSegmentStore store,
final boolean dumpLeafState) {
final long begin = System.currentTimeMillis();
final AbstractBTree btree = store.loadIndexSegment();
// first the address of the first leaf in a right-to-left scan (always defined).
long addr = store.getCheckpoint().addrLastLeaf;
System.out.println("lastLeafAddr="+store.toString(addr));
{
final long addr2 = getLastLeafAddr(store,
store.getCheckpoint().addrRoot);
if (addr != addr2) {
log.error("Last leaf address is inconsistent? checkpoint reports: "
+ addr
+ " ("
+ store.toString(addr)
+ ")"
+ ", but node hierarchy reports "
+ addr2
+ " ("
+ store.toString(addr2) + ")");
}
}
int nscanned = 0;
while (true) {
if(!store.getAddressManager().isLeafAddr(addr)) {
log.error("Not a leaf address: "+store.toString(addr)+" : aborting scan");
// abort scan.
break;
}
// lower level read
final ByteBuffer data = store.read(addr);
// note: does NOT set the parent reference on the Leaf!
final Leaf leaf = (Leaf) decode(btree, addr, data);
if(dumpLeafState) leaf.dump(System.out);
nscanned++;
final long priorAddr = ((ImmutableLeaf)leaf).getPriorAddr();
if (priorAddr == -1L) {
log.error("Expecting the prior address to be known - aborting scan: current addr="
+ addr+" ("+store.toString(addr)+")");
// abort scan.
break;
}
if(priorAddr == 0L) {
if (nscanned != store.getCheckpoint().nleaves) {
log.error("Scanned "
+ nscanned
+ " leaves, but checkpoint record indicates that there are "
+ store.getCheckpoint().nleaves + " leaves");
}
// Done (normal completion).
break;
}
// go to the previous leaf in the key order.
addr = priorAddr;
}
final long elapsed = System.currentTimeMillis() - begin;
System.out.println("Visited "+nscanned+" leaves using fast reverse scan in "+elapsed+" ms");
}
/**
* Dump leaves by direct record scan from first leaf offset until end of
* leaves region.
*
* Note: While this could be rewritten for cleaner code to use
* {@link IndexSegment#leafIterator(boolean)} but it would make it harder to
* spot problems in the data.
*
* @param store
*/
static void dumpLeavesForwardScan(final IndexSegmentStore store,
final boolean dumpLeafState) {
final long begin = System.currentTimeMillis();
final AbstractBTree btree = store.loadIndexSegment();
// first the address of the first leaf in a left-to-right scan (always defined).
long addr = store.getCheckpoint().addrFirstLeaf;
{
final long addr2 = getFirstLeafAddr(store,
store.getCheckpoint().addrRoot);
if (addr != addr2) {
log.error("First leaf address is inconsistent? checkpoint reports: "
+ addr
+ " ("
+ store.toString(addr)
+ ")"
+ ", but node hierarchy reports "
+ addr2
+ " ("
+ store.toString(addr2) + ")");
}
}
System.out.println("firstLeafAddr="+store.toString(addr));
int nscanned = 0;
while (true) {
if(!store.getAddressManager().isLeafAddr(addr)) {
log.error("Not a leaf address: "+store.toString(addr)+" : aborting scan");
// abort scan.
break;
}
// lower level read
final ByteBuffer data = store.read(addr);
// note: does NOT set the parent reference on the Leaf!
final Leaf leaf = (Leaf) decode(btree, addr, data);
if(dumpLeafState) leaf.dump(System.out);
nscanned++;
final long nextAddr = ((ImmutableLeaf)leaf).getNextAddr();
if (nextAddr == -1L) {
log.error("Expecting the next address to be known - aborting scan: current addr="
+ addr+" ("+store.toString(addr)+")");
// abort scan.
break;
}
if(nextAddr == 0L) {
if (nscanned != store.getCheckpoint().nleaves) {
log.error("Scanned "
+ nscanned
+ " leaves, but checkpoint record indicates that there are "
+ store.getCheckpoint().nleaves + " leaves");
}
// Done (normal completion).
break;
}
addr = nextAddr;
}
final long elapsed = System.currentTimeMillis() - begin;
System.out.println("Visited "+nscanned+" leaves using fast forward scan in "+elapsed+" ms");
}
/**
* Dump leaves using the {@link IndexSegmentMultiBlockIterator}.
*
* @param store
*/
static void dumpLeavesMultiBlockForwardScan(final IndexSegmentStore store) {
final long begin = System.currentTimeMillis();
final IndexSegment seg = store.loadIndexSegment();
final ITupleIterator> itr = new IndexSegmentMultiBlockIterator(seg, DirectBufferPool.INSTANCE,
null/* fromKey */, null/* toKey */, IRangeQuery.DEFAULT/* flags */);
int nscanned = 0;
while(itr.hasNext()) {
itr.next();
nscanned++;
}
final long elapsed = System.currentTimeMillis() - begin;
System.out.println("Visited "+nscanned+" tuples using multi-block forward scan in "+elapsed+" ms");
}
static void writeBanner(String s) {
System.out.println(bar);
System.out.println("=== "+s);
System.out.println(bar);
}
static final String bar = "============================================================";
}