Package com.persistit

Source Code of com.persistit.JournalTool

/**
* Copyright 2005-2012 Akiban Technologies, Inc.
*
* 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 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 com.persistit;

import static com.persistit.JournalRecord.OVERHEAD;
import static com.persistit.JournalRecord.SUB_RECORD_OVERHEAD;
import static com.persistit.JournalRecord.getLength;
import static com.persistit.JournalRecord.getTimestamp;
import static com.persistit.JournalRecord.getType;
import static com.persistit.JournalRecord.isValidType;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.persistit.JournalRecord.CP;
import com.persistit.JournalRecord.D0;
import com.persistit.JournalRecord.D1;
import com.persistit.JournalRecord.DR;
import com.persistit.JournalRecord.DT;
import com.persistit.JournalRecord.IT;
import com.persistit.JournalRecord.IV;
import com.persistit.JournalRecord.JE;
import com.persistit.JournalRecord.JH;
import com.persistit.JournalRecord.PA;
import com.persistit.JournalRecord.PM;
import com.persistit.JournalRecord.SR;
import com.persistit.JournalRecord.TM;
import com.persistit.JournalRecord.TX;
import com.persistit.exception.CorruptJournalException;
import com.persistit.exception.PersistitException;
import com.persistit.exception.PersistitIOException;
import com.persistit.mxbeans.JournalManagerMXBean;
import com.persistit.util.ArgParser;

/**
* @author Peter Beaman
* @version 1.0
*/
class JournalTool {

    public final static int DEFAULT_BUFFER_SIZE = 1 * 1024 * 1024;

    public final static long DEFAULT_BLOCK_SIZE = 1000000000L;

    private final static int EOF = -1;

    private final static int EOJ = -2;

    final static String[] ARGS_TEMPLATE = { "path|string:|Journal file name",
            "start|long:0:0:10000000000000|Start journal address",
            "end|long:1000000000000000000:0:1000000000000000000|End journal address",
            "types|String:*|Selected record types, for example, \"PA,PM,CP\"",
            "pages|String:*|Selected pages, for example, \"0,1,200-299,33333-\"",
            "timestamps|String:*|Selected timestamps, for example, \"132466-132499\"",
            "maxkey|int:42:4:10000|Maximum displayed key length",
            "maxvalue|int:42:4:100000|Maximum displayed value length",
            "timestamps|String:*|Selected timestamps, for example, \"132466-132499\"",
            "_flag|v|Verbose dump - includes PageMap and TransactionMap details" };

    private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");

    private final Persistit _persistit;

    private Action _action;

    private String _journalFilePath;

    private long _startAddr;

    private long _endAddr;

    private PrintWriter _writer = new PrintWriter(new OutputStreamWriter(System.out));

    private final Map<Long, FileChannel> _journalFileChannels = new HashMap<Long, FileChannel>();

    private ByteBuffer _readBuffer;

    private final int _readBufferSize = DEFAULT_BUFFER_SIZE;

    private long _readBufferAddress;

    private long _currentAddress;

    private long _blockSize = DEFAULT_BLOCK_SIZE;

    private BitSet _selectedTypes = new BitSet(65536);

    private RangePredicate _selectedPages;

    private RangePredicate _selectedTimestamps;

    private int _keyDisplayLength;

    private int _valueDisplayLength;

    private boolean _verbose;

    public long getStartAddr() {
        return _startAddr;
    }

    public void setStartAddr(final long startAddr) {
        _startAddr = startAddr;
    }

    public long getEndAddr() {
        return _endAddr;
    }

    public void setEndAddr(final long endAddr) {
        _endAddr = endAddr;
    }

    public Action getAction() {
        return _action;
    }

    public void setAction(final Action action) {
        _action = action;
    }

    public PrintWriter getWriter() {
        return _writer;
    }

    public void setWriter(final PrintWriter writer) {
        _writer = writer;
    }

    public BitSet getSelectedTypes() {
        return _selectedTypes;
    }

    public void setSelectedTypes(final BitSet selectedTypes) {
        _selectedTypes = selectedTypes;
    }

    public RangePredicate getSelectedPages() {
        return _selectedPages;
    }

    public void setSelectedPages(final RangePredicate selectedPages) {
        _selectedPages = selectedPages;
    }

    public RangePredicate getSelectedTimestamps() {
        return _selectedTimestamps;
    }

    public void setSelectedTimestamps(final RangePredicate selectedTimestamps) {
        _selectedTimestamps = selectedTimestamps;
    }

    public int getKeyDisplayLength() {
        return _keyDisplayLength;
    }

    public void setKeyDisplayLength(final int keyDisplayLength) {
        _keyDisplayLength = keyDisplayLength;
    }

    public int getValueDisplayLength() {
        return _valueDisplayLength;
    }

    public void setValueDisplayLength(final int valueDisplayLength) {
        _valueDisplayLength = valueDisplayLength;
    }

    public boolean isVerbose() {
        return _verbose;
    }

    public void setVerbose(final boolean verbose) {
        _verbose = verbose;
    }

    public interface Action {
        public void je(final long address, final long timestamp, final int recordSize) throws Exception;

        public void jh(final long address, final long timestamp, final int recordSize) throws Exception;

        public void iv(final long address, final long timestamp, final int recordSize) throws Exception;

        public void it(final long address, final long timestamp, final int recordSize) throws Exception;

        public void cp(final long address, final long timestamp, final int recordSize) throws Exception;

        public void tx(final long address, final long timestamp, final int recordSize) throws Exception;

        public void sr(final long address, final long timestamp, final int recordSize) throws Exception;

        public void dr(final long address, final long timestamp, final int recordSize) throws Exception;

        public void dt(final long address, final long timestamp, final int recordSize) throws Exception;

        public void pa(final long address, final long timestamp, final int recordSize) throws Exception;

        public void pm(final long address, final long timestamp, final int recordSize) throws Exception;

        public void tm(final long address, final long timestamp, final int recordSize) throws Exception;

        public void d0(final long address, final long timestamp, final int recordSize) throws Exception;

        public void d1(final long address, final long timestamp, final int recordSize) throws Exception;

        public void eof(final long address) throws Exception;
    }

    protected static class RangePredicate {
        private final long[] _left;
        private final long[] _right;

        RangePredicate(final String ps) {
            if ("*".equals(ps)) {
                _left = new long[0];
                _right = new long[0];
            } else {
                final String[] terms = ps.split(",");
                _left = new long[terms.length];
                _right = new long[terms.length];
                for (int index = 0; index < terms.length; index++) {
                    final String[] range = terms[index].split("-");
                    boolean okay = true;
                    try {
                        switch (range.length) {
                        case 1:
                            final long v = Long.parseLong(range[0]);
                            _left[index] = v;
                            _right[index] = v;
                            break;

                        case 2:
                            if (range[0].isEmpty()) {
                                _left[index] = Long.MIN_VALUE;
                            } else {
                                _left[index] = Long.parseLong(range[0]);
                            }
                            if (range[1].isEmpty()) {
                                _right[index] = Long.MAX_VALUE;
                            } else {
                                _right[index] = Long.parseLong(range[1]);
                            }
                            break;
                        default:
                            okay = false;
                        }
                    } catch (final NumberFormatException e) {
                        okay = false;
                    }
                    if (!okay) {
                        throw new IllegalArgumentException("Invalid term " + terms[index] + " in range specification "
                                + ps);
                    }
                }
            }
        }

        private boolean isSelected(final long v) {
            if (_left.length == 0) {
                return true;
            }
            for (int index = 0; index < _left.length; index++) {
                if (_left[index] <= v && _right[index] >= v) {
                    return true;
                }
            }
            return false;
        }
    }

    public JournalTool(final Persistit persistit) {
        _persistit = persistit;
        _action = new SimpleDumpAction();
        _selectedTypes.set(0, 65535, true);
    }

    public void init(final String[] args) {
        final ArgParser ap = new ArgParser("com.persistit.JournalTool", args, ARGS_TEMPLATE).strict();
        if (ap.isUsageOnly()) {
            return;
        }
        init(ap);
    }

    void init(final ArgParser ap) {
        init(ap.getStringValue("path"), ap.getLongValue("start"), ap.getLongValue("end"), ap.getStringValue("types"),
                ap.getStringValue("pages"), ap.getStringValue("timestamps"), ap.getIntValue("maxkey"),
                ap.getIntValue("maxvalue"), ap.isFlag('v'));
    }

    void init(final String path, final long start, final long end, final String types, final String pages,
            final String timestamps, final int maxkey, final int maxvalue, final boolean v) {
        _startAddr = start;
        _endAddr = end;
        String pathName = path;
        if (isNullOrEmpty(pathName) && _persistit != null) {
            pathName = _persistit.getJournalManager().getJournalFilePath();
        }
        if (isNullOrEmpty(pathName)) {
            throw new IllegalArgumentException(
                    "The 'path' parameter must specify a valid journal path, for example, \n"
                            + " /xxx/yyy/jjj where journal file names " + "are like jjj.000000001234");
        }
        final long generation = JournalManager.fileToGeneration(new File(pathName));
        if (generation != -1) {
            pathName = pathName.substring(0, pathName.lastIndexOf('.'));
            if (start == 0) {
                _startAddr = generation * JournalManagerMXBean.DEFAULT_BLOCK_SIZE;
            }
            if (end == 1000000000000000000l) {
                _endAddr = (generation + 1) * JournalManagerMXBean.DEFAULT_BLOCK_SIZE;
            }
        }
        _journalFilePath = pathName;
        _readBuffer = ByteBuffer.allocate(_readBufferSize);
        parseTypes(types);
        _selectedPages = new RangePredicate(pages);
        _selectedTimestamps = new RangePredicate(timestamps);
        _keyDisplayLength = maxkey;
        _valueDisplayLength = maxvalue;
        _verbose = v;

    }

    private boolean isNullOrEmpty(final String string) {
        return string == null || string.isEmpty();
    }

    private void parseTypes(final String types) {
        boolean okay = true;
        if (!("*".equals(types))) {
            _selectedTypes.set(0, _selectedTypes.size(), false);
            for (final String typeString : types.split(",")) {
                if (typeString.length() == 2) {
                    final int t = (typeString.charAt(0) << 8) | typeString.charAt(1);
                    if (JournalRecord.isValidType(t)) {
                        _selectedTypes.set(t, true);
                    } else {
                        okay = false;
                    }
                } else {
                    okay = false;
                }
            }
        }
        if (!okay) {
            throw new IllegalArgumentException("The 'types' parameter must specify either \"*\" or a "
                    + "comma-separated list of valid record type names");
        }
    }

    public void scan() throws Exception {
        try {
            _currentAddress = _startAddr;
            _readBufferAddress = Long.MIN_VALUE;
            while (_currentAddress < _endAddr) {
                final int type = scanOneRecord();
                switch (type) {
                case JE.TYPE:
                case EOF:
                    _currentAddress = addressUp(_currentAddress);
                    break;
                case EOJ:
                    return;
                }
            }
        } finally {
            _writer.flush();
        }
    }

    /**
     * Attempts to read and apply the record at _currentAddress. If it finds
     * valid record contained in the current journal file, it advances the
     * _currentAddress to the start of the next record and returns the type of
     * the record. Otherwise this method does nothing and returns -1;
     *
     * @return The record type: one of the type values specified in
     *         {@link com.persistit.JournalRecord}), 0 if the journal file has
     *         fewer than 16 bytes remaining or -t where t is an invalid type.
     * @throws CorruptJournalException
     * @throws PersistitException
     * @throws JournalNotClosedException
     */
    private int scanOneRecord() throws Exception {

        final long from = _currentAddress;
        try {
            read(_currentAddress, OVERHEAD);
        } catch (final CorruptJournalException e) {
            // This exception means abnormal end-of-file.
            _action.eof(from);
            return EOF;
        } catch (final PersistitIOException e) {
            if (e.getCause() instanceof FileNotFoundException) {
                return EOJ;
            } else {
                throw e;
            }
        }
        final int recordSize = getLength(_readBuffer);
        final int type = getType(_readBuffer);
        final long timestamp = getTimestamp(_readBuffer);
        _currentAddress = processOneRecord(from, timestamp, recordSize, type);
        return type;
    }

    long processOneRecord(final long from, final long timestamp, final int recordSize, final int type) throws Exception {
        if (recordSize >= _blockSize || recordSize < SUB_RECORD_OVERHEAD) {
            throw new CorruptJournalException("Bad JournalRecord length " + recordSize + " at position "
                    + addressToString(from, timestamp));
        }
        long address = from;
        switch (type) {

        case JE.TYPE:
            if (_selectedTypes.get(type)) {
                _action.je(address, timestamp, recordSize);
            }
            break;

        case JH.TYPE:
            read(_currentAddress, recordSize);
            final long blockSize = JH.getBlockSize(_readBuffer);
            if (blockSize != _blockSize) {
                address = _currentAddress = (_currentAddress / _blockSize) * blockSize;
                _readBufferAddress = _currentAddress;
                _blockSize = blockSize;
            }
            if (_selectedTypes.get(type)) {
                _action.jh(address, timestamp, recordSize);
            }
            break;

        case SR.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.sr(address, timestamp, recordSize);
            }
            break;

        case DR.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.dr(address, timestamp, recordSize);
            }
            break;

        case DT.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.dt(address, timestamp, recordSize);
            }
            break;

        case IV.TYPE:
            if (_selectedTypes.get(type)) {
                _action.iv(address, timestamp, recordSize);
            }
            break;

        case IT.TYPE:
            if (_selectedTypes.get(type)) {
                _action.it(address, timestamp, recordSize);
            }
            break;

        case PA.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.pa(address, timestamp, recordSize);
            }
            break;

        case PM.TYPE:
            if (_selectedTypes.get(type)) {
                _action.pm(address, timestamp, recordSize);
            }
            break;

        case TM.TYPE:
            if (_selectedTypes.get(type)) {
                _action.tm(address, timestamp, recordSize);
            }
            break;

        case TX.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.tx(address, timestamp, recordSize);
            }
            break;

        case CP.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.cp(address, timestamp, recordSize);
            }
            break;

        case D0.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.d0(address, timestamp, recordSize);
            }
            break;

        case D1.TYPE:
            if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
                _action.d1(address, timestamp, recordSize);
            }
            break;

        default:
            if (!isValidType(type)) {
                _currentAddress -= OVERHEAD;
                throw new CorruptJournalException("Invalid record type " + type + " at " + addressToString(address));
            }
        }
        return address + recordSize;

    }

    private long addressUp(final long address) {
        return ((address / _blockSize) + 1) * _blockSize;
    }

    private synchronized FileChannel getFileChannel(final long address) throws PersistitIOException {
        final long generation = address / _blockSize;
        FileChannel channel = _journalFileChannels.get(generation);
        if (channel == null) {
            try {
                final RandomAccessFile raf = new RandomAccessFile(addressToFile(address), "r");
                channel = raf.getChannel();
                _journalFileChannels.put(generation, channel);
            } catch (final IOException ioe) {
                throw new PersistitIOException(ioe);
            }
        }
        return channel;
    }

    File addressToFile(final long address) {
        return JournalManager.generationToFile(_journalFilePath, address / _blockSize);
    }

    private void read(final long address, final int size) throws PersistitIOException {
        if (_readBufferAddress >= 0 && address >= _readBufferAddress
                && size + address - _readBufferAddress <= _readBuffer.limit()) {
            _readBuffer.position((int) (address - _readBufferAddress));
        } else {
            try {
                final FileChannel fc = getFileChannel(address);
                _readBuffer.clear();

                int maxSize = _readBuffer.capacity();
                final long remainingInBlock = addressUp(address) - address;
                if (remainingInBlock < maxSize) {
                    maxSize = (int) remainingInBlock;
                }

                _readBuffer.limit(maxSize);
                // Try to read up to maxSize bytes into _readBuffer
                int offset = 0;
                while (_readBuffer.remaining() > 0) {
                    final int readSize = fc.read(_readBuffer, offset + address % _blockSize);
                    if (readSize < 0) {
                        break;
                    }
                    offset += readSize;
                }
                _readBufferAddress = address;
                _readBuffer.flip();
                if (_readBuffer.remaining() < size) {
                    throw new CorruptJournalException("End of file at " + addressToString(address));
                }
            } catch (final IOException e) {
                throw new PersistitIOException("Reading from " + addressToString(address), e);
            }
        }
    }

    private String addressToString(final long address) {
        return String.format("JournalAddress %,d", address);
    }

    private String addressToString(final long address, final long timestamp) {
        return String.format("JournalAddress %,d{%,d}", address, timestamp);
    }

    // -----------------------

    public static void main(final String[] args) throws Exception {
        final Persistit persistit = new Persistit();
        JournalTool jt = null;
        try {
            jt = new JournalTool(persistit);
            jt.init(args);
        } catch (final IllegalArgumentException e) {
            System.err.println(e.getLocalizedMessage());
            System.exit(1);
        }
        try {
            jt.scan();
        } catch (final Exception e) {
            e.printStackTrace();
        } finally {
            persistit.close();
        }
    }

    protected class SimpleDumpAction implements Action {

        final static String ELIPSES = "...";

        final StringBuilder sb = new StringBuilder();

        final Key key1 = new Key(_persistit);

        final Key key2 = new Key(_persistit);

        final Value value = new Value(_persistit);

        protected void start(final long address, final long timestamp, final String type, final int recordSize) {
            sb.setLength(0);
            sb.append(String.format("%,18d%,16d %2s (%8d) ", address, timestamp, type, recordSize));
        }

        protected void appendf(final String format, final Object... args) {
            sb.append(String.format(format, args));
        }

        protected void keyf(final Key key) {
            String s = null;
            try {
                s = key.toString();
            } catch (final Exception e) {
                s = e.getLocalizedMessage();
            }
            padf(s, _keyDisplayLength);
        }

        protected void valuef(final Value value) {
            String s = null;
            try {
                if (value.getEncodedSize() >= Buffer.LONGREC_SIZE
                        && (value.getEncodedBytes()[0] & 0xFF) == Buffer.LONGREC_TYPE) {
                    final long page = Buffer.decodeLongRecordDescriptorPointer(value.getEncodedBytes(), 0);
                    final int size = Buffer.decodeLongRecordDescriptorSize(value.getEncodedBytes(), 0);
                    s = String.format("LONG_REC size %,8d page %12d", size, page);
                } else {
                    s = value.toString();
                }
            } catch (final Exception e) {
                s = e.getLocalizedMessage();
            }
            padf(s, _valueDisplayLength);
        }

        protected void padf(final String s, final int length) {
            if (s.length() < length) {
                sb.append(s);
                for (int i = length - s.length(); --i >= 0;) {
                    sb.append(' ');
                }
            } else {
                sb.append(s.substring(0, length - ELIPSES.length()));
                sb.append(ELIPSES);
            }
        }

        protected void flush() {
            if (sb.length() > 0) {
                write(sb.toString());
                sb.setLength(0);
            }
        }

        protected void write(final String msg) {
            _writer.println(msg);
        }

        // -----------------------

        @Override
        public void jh(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final long baseAddress = JH.getBaseJournalAddress(_readBuffer);
            final long blockSize = JH.getBlockSize(_readBuffer);
            final String fileCreated = SDF.format(new Date(JH.getFileCreatedTime(_readBuffer)));
            final String journalCreated = SDF.format(new Date(JH.getJournalCreatedTime(_readBuffer)));
            final long version = JH.getVersion(_readBuffer);
            start(address, timestamp, "JH", recordSize);
            appendf(" version %,3d blockSize %,14d baseAddress %,18d journalCreated %24s fileCreated %24s", version,
                    blockSize, baseAddress, journalCreated, fileCreated);
            _blockSize = blockSize;
            flush();
        }

        @Override
        public void je(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            start(address, timestamp, "JE", recordSize);
            final long baseAddress = JE.getBaseAddress(_readBuffer);
            final long currentAddress = JE.getCurrentJournalAddress(_readBuffer);
            final String journalCreated = SDF.format(new Date(JE.getJournalCreatedTime(_readBuffer)));
            appendf(" baseAddress %,18d currentAddress %,18d journalCreated %24s", baseAddress, currentAddress,
                    journalCreated);
            flush();
        }

        @Override
        public void iv(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final int handle = IV.getHandle(_readBuffer);
            final long id = IV.getVolumeId(_readBuffer);
            final String name = IV.getVolumeSpecification(_readBuffer);
            start(address, timestamp, "IV", recordSize);
            appendf(" handle %05d id %,22d name %s", handle, id, name);
            flush();
        }

        @Override
        public void it(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final int handle = IT.getHandle(_readBuffer);
            final int vhandle = IT.getVolumeHandle(_readBuffer);
            final String treeName = IT.getTreeName(_readBuffer);
            start(address, timestamp, "IT", recordSize);
            appendf(" handle %05d volume %05d treeName %s", handle, vhandle, treeName);
            flush();
        }

        @Override
        public void cp(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final long baseAddress = CP.getBaseAddress(_readBuffer);
            final String wallTime = SDF.format(new Date(CP.getSystemTimeMillis(_readBuffer)));
            start(address, timestamp, "CP", recordSize);
            appendf(" baseAddress %,18d at %24s", baseAddress, wallTime);
            flush();
        }

        @Override
        public void tx(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            start(address, timestamp, "TX", recordSize);
            appendf(" committed %,d backchain %,d", TX.getCommitTimestamp(_readBuffer),
                    TX.getBackchainAddress(_readBuffer));
            flush();
            final int start = _readBuffer.position();
            final int end = start + recordSize;
            _readBuffer.position(_readBuffer.position() + TX.OVERHEAD);
            while (_readBuffer.position() < end) {
                final int innerSize = getLength(_readBuffer);
                final int type = getType(_readBuffer);
                final int position = _readBuffer.position();
                processOneRecord(address + position - start, timestamp, innerSize, type);
                _readBuffer.position(position + innerSize);
            }
            flush();
        }

        @Override
        public void dr(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final int thandle = DR.getTreeHandle(_readBuffer);
            final int key1Size = DR.getKey1Size(_readBuffer);
            final int elisionCount = DR.getKey2Elision(_readBuffer);
            final int key2Size = recordSize - key1Size - DR.OVERHEAD;
            System.arraycopy(_readBuffer.array(), _readBuffer.position() + DR.OVERHEAD, key1.getEncodedBytes(), 0,
                    key1Size);
            key1.setEncodedSize(key1Size);
            System.arraycopy(key1.getEncodedBytes(), 0, key2.getEncodedBytes(), 0, elisionCount);
            System.arraycopy(_readBuffer.array(), _readBuffer.position() + DR.OVERHEAD + key1Size,
                    key2.getEncodedBytes(), elisionCount, key2Size);
            key2.setEncodedSize(key2Size + elisionCount);
            start(address, timestamp, "DR", recordSize);
            appendf(" tree %05d key1Size %,5d key2Size %,5d  ", thandle, key1Size, key2Size);
            keyf(key1);
            sb.append("->");
            keyf(key2);
            flush();
        }

        @Override
        public void sr(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final int thandle = SR.getTreeHandle(_readBuffer);
            final int keySize = SR.getKeySize(_readBuffer);
            final int valueSize = recordSize - keySize - SR.OVERHEAD;
            System.arraycopy(_readBuffer.array(), _readBuffer.position() + SR.OVERHEAD, key1.getEncodedBytes(), 0,
                    keySize);
            key1.setEncodedSize(keySize);
            value.ensureFit(valueSize);
            System.arraycopy(_readBuffer.array(), _readBuffer.position() + SR.OVERHEAD + keySize,
                    value.getEncodedBytes(), 0, valueSize);
            value.setEncodedSize(valueSize);
            start(address, timestamp, "SR", recordSize);
            appendf(" tree %05d keySize %,5d valueSize %,5d  ", thandle, keySize, valueSize);
            keyf(key1);
            sb.append(" : ");
            valuef(value);
            flush();
        }

        @Override
        public void dt(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, DT.OVERHEAD);
            final int thandle = DT.getTreeHandle(_readBuffer);
            start(address, timestamp, "DT", recordSize);
            appendf(" tree %05d", thandle);
            flush();
        }

        @Override
        public void pa(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            final long pageAddress = PA.getPageAddress(_readBuffer);
            final int volumeHandle = PA.getVolumeHandle(_readBuffer);
            if (!_selectedPages.isSelected(pageAddress)) {
                return;
            }
            start(address, timestamp, "PA", recordSize);
            final int type = JournalRecord.getByte(_readBuffer, PA.OVERHEAD + Buffer.TYPE_OFFSET);
            final String typeString = Buffer.getPageTypeName(pageAddress, type);
            final long rightSibling = pageAddress == 0 ? 0 : JournalRecord.getLong(_readBuffer, PA.OVERHEAD
                    + Buffer.RIGHT_SIBLING_OFFSET);
            appendf(" page %5d:%,12d type %10s right %,12d", volumeHandle, pageAddress, typeString, rightSibling);
            flush();
        }

        @Override
        public void pm(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, PM.OVERHEAD);
            start(address, timestamp, "PM", recordSize);
            final int count = PM.getEntryCount(_readBuffer);
            appendf(" entries %,10d", count);
            flush();
            if (_verbose) {
                dumpPageMap(count, address, timestamp, recordSize);
                flush();
            }
        }

        @Override
        public void tm(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, TM.OVERHEAD);
            start(address, timestamp, "TM", recordSize);
            final int count = TM.getEntryCount(_readBuffer);
            appendf(" entries %,10d", count);
            flush();
            if (_verbose) {
                dumpTransactionMap(count, address, timestamp, recordSize);
                flush();
            }
        }

        @Override
        public void d0(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            start(address, timestamp, "D0", recordSize);
            final int thandle = D0.getTreeHandle(_readBuffer);
            final int index = D0.getIndex(_readBuffer);
            appendf(" tree %05d index %2d value %,5d", thandle, index, 1);
            flush();
        }

        @Override
        public void d1(final long address, final long timestamp, final int recordSize) throws Exception {
            read(address, recordSize);
            start(address, timestamp, "D1", recordSize);
            final int thandle = D1.getTreeHandle(_readBuffer);
            final int index = D1.getIndex(_readBuffer);
            final long value = D1.getValue(_readBuffer);
            appendf(" tree %05d index %2d value %,5d", thandle, index, value);
            flush();
        }

        @Override
        public void eof(final long address) throws Exception {
            start(address, 0, "~~", 0);
            flush();
        }

        void dumpPageMap(final int count, final long from, final long timestamp, final int recordSize)
                throws PersistitIOException {
            if (count * PM.ENTRY_SIZE + PM.OVERHEAD != recordSize) {
                throw new CorruptJournalException("Invalid record size " + recordSize + " for PM record at "
                        + addressToString(from, timestamp));
            }

            long address = from + PM.OVERHEAD;
            int index = 0;
            int loaded = 0;
            long lastPage = Long.MAX_VALUE;
            int lastVolumeHandle = Integer.MAX_VALUE;
            for (int remaining = count; remaining > 0; remaining--) {
                if (index == loaded) {
                    final int loadedSize = Math.min((_readBuffer.capacity() / PM.ENTRY_SIZE) * PM.ENTRY_SIZE, remaining
                            * PM.ENTRY_SIZE);
                    read(address, loadedSize);
                    address += loadedSize;
                    index = 0;
                    loaded = loadedSize / PM.ENTRY_SIZE;
                    if (loaded <= 0) {
                        throw new CorruptJournalException("Could not load PageMap segment in entry "
                                + (count - remaining + 1) + " at " + addressToString(from, timestamp));
                    }
                }
                final int volumeHandle = PM.getEntryVolumeHandle(_readBuffer, index);
                final long pageAddress = PM.getEntryPageAddress(_readBuffer, index);
                final long pageTimestamp = PM.getEntryTimestamp(_readBuffer, index);
                final long journalAddress = PM.getEntryJournalAddress(_readBuffer, index);
                if (_selectedPages.isSelected(pageAddress) && _selectedTimestamps.isSelected(pageTimestamp)) {
                    if (pageAddress != lastPage || volumeHandle != lastVolumeHandle) {
                        flush();
                        lastPage = pageAddress;
                        lastVolumeHandle = volumeHandle;
                        appendf("-- %5d:%,12d: ", volumeHandle, pageAddress);
                    }
                    appendf(" @%,d(%,d)", journalAddress, pageTimestamp);
                }
                index++;
            }
        }

        void dumpTransactionMap(final int count, final long from, final long timestamp, final int recordSize)
                throws PersistitIOException {
            if (count * TM.ENTRY_SIZE + TM.OVERHEAD != recordSize) {
                throw new CorruptJournalException("Invalid record size " + recordSize + " for TM record at "
                        + addressToString(from, timestamp));
            }
            long address = from + TM.OVERHEAD;
            int index = 0;
            int loaded = 0;
            for (int remaining = count; remaining > 0; remaining--) {
                if (index == loaded) {
                    read(address, Math.min(_readBuffer.capacity(), remaining * TM.ENTRY_SIZE));
                    address += _readBuffer.remaining();
                    index = 0;
                    loaded = _readBuffer.remaining() / TM.ENTRY_SIZE;
                    if (loaded <= 0) {
                        throw new CorruptJournalException("Could not load TransactionMap segment in entry "
                                + (count - remaining + 1) + " at " + addressToString(from, timestamp));
                    }
                }
                final long startTimestamp = TM.getEntryStartTimestamp(_readBuffer, index);
                final long commitTimestamp = TM.getEntryCommitTimestamp(_readBuffer, index);
                final long journalAddress = TM.getEntryJournalAddress(_readBuffer, index);
                final boolean isCommitted = commitTimestamp != 0;
                appendf("--  start %,12d  commit %,12d  @%,18d %s", startTimestamp, commitTimestamp, journalAddress,
                        isCommitted ? "committed" : "uncommitted");
                flush();
                index++;
            }
        }
    }
}
TOP

Related Classes of com.persistit.JournalTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.