All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.journal.RootBlockUtility 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 Nov 3, 2010
 */

package com.bigdata.journal;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.UUID;

import org.apache.log4j.Logger;

import com.bigdata.io.ChecksumUtility;
import com.bigdata.io.FileChannelUtility;
import com.bigdata.io.IReopenChannel;

/**
 * Utility class will read both root blocks of a file and indicate which one
 * is current.
 * 
 * @author Bryan
 *         Thompson
 * @version $Id$
 */
public class RootBlockUtility {

    private static final Logger log = Logger.getLogger(RootBlockUtility.class);

    /**
     * The 1st root block.
     */
    public final IRootBlockView rootBlock0;

    /**
     * The 2nd root block.
     */
    public final IRootBlockView rootBlock1;

    /**
     * The current root block. For a new file, this is "rootBlock0". For an
     * existing file it is based on an examination of both root blocks.
     */
    public final IRootBlockView rootBlock;

    /**
     * 
     * @param opener
     * @param file
     * @param validateChecksum
     * @param alternateRootBlock
     *            When true, the alternate root block will be
     *            chosen. This flag only makes sense when you have two root
     *            blocks to choose from and you want to choose the other one.
     * @param ignoreBadRootBlock
     *            When true, a bad root block will be ignored.
     * 
     * @throws IOException
     * 
     * @see com.bigdata.journal.Options#ALTERNATE_ROOT_BLOCK
     * @see com.bigdata.journal.Options#IGNORE_BAD_ROOT_BLOCK
     */
    public RootBlockUtility(final IReopenChannel opener,
            final File file, final boolean validateChecksum,
            final boolean alternateRootBlock, final boolean ignoreBadRootBlock)
            throws IOException {

        final ChecksumUtility checker = validateChecksum ? ChecksumUtility.threadChk
                .get()
                : null;

        final ByteBuffer tmp0 = ByteBuffer
                .allocate(RootBlockView.SIZEOF_ROOT_BLOCK);
        final ByteBuffer tmp1 = ByteBuffer
                .allocate(RootBlockView.SIZEOF_ROOT_BLOCK);
        FileChannelUtility.readAll(opener, tmp0, FileMetadata.OFFSET_ROOT_BLOCK0);
        FileChannelUtility.readAll(opener, tmp1, FileMetadata.OFFSET_ROOT_BLOCK1);
        tmp0.position(0); // resets the position.
        tmp1.position(0);
        IRootBlockView rootBlock0 = null, rootBlock1 = null;
        try {
            rootBlock0 = new RootBlockView(true, tmp0, checker);
            if (log.isInfoEnabled())
                log.info("rootBlock0: " + rootBlock0);
        } catch (RootBlockException ex) {
            log.error("Bad root block zero: " + ex);
        }
        try {
            rootBlock1 = new RootBlockView(false, tmp1, checker);
            if (log.isInfoEnabled())
                log.info("rootBlock1: " + rootBlock1);
        } catch (RootBlockException ex) {
            log.error("Bad root block one: " + ex);
        }
        if (rootBlock0 == null && rootBlock1 == null) {
            throw new RuntimeException(
                    "Both root blocks are bad - journal is not usable: " + file);
        }

        // save references.
        this.rootBlock0 = rootBlock0;
        this.rootBlock1 = rootBlock1;

        this.rootBlock = chooseRootBlock(rootBlock0, rootBlock1,
                ignoreBadRootBlock, alternateRootBlock);
    }
    
    public RootBlockUtility(final IRootBlockView rb0, final IRootBlockView rb1) {
        this.rootBlock0 = rb0;
        this.rootBlock1 = rb1;
        this.rootBlock = chooseRootBlock(rootBlock0, rootBlock1,
                false/* ignoreBadRootBlock */, false/* alternateRootBlock */);
    }

    /**
     * Return the chosen root block. The root block having the greater
     * {@link IRootBlockView#getCommitCounter() commit counter} is chosen by
     * default.
     * 

* Note: For historical compatibility, rootBlock1 is chosen if * both root blocks have the same {@link IRootBlockView#getCommitCounter()}. * * @param rootBlock0 * Root block 0 (may be null if this root block is bad). * @param rootBlock1 * Root block 1 (may be null if this root block is bad). * * @return The chosen root block. * * @throws RuntimeException * if no root block satisfies the criteria. */ public static IRootBlockView chooseRootBlock( final IRootBlockView rootBlock0, final IRootBlockView rootBlock1) { return chooseRootBlock(rootBlock0, rootBlock1, false/* alternateRootBlock */, false/* ignoreBadRootBlock */); } /** * Return the chosen root block. The root block having the greater * {@link IRootBlockView#getCommitCounter() commit counter} is chosen by * default. *

* Note: For historical compatibility, rootBlock1 is chosen if * both root blocks have the same {@link IRootBlockView#getCommitCounter()}. * * @return The chosen root block. * * @throws RuntimeException * if no root block satisfies the criteria. */ public IRootBlockView chooseRootBlock() { return chooseRootBlock(rootBlock0, rootBlock1, false/* alternateRootBlock */, false/* ignoreBadRootBlock */); } /** * Return the chosen root block. The root block having the greater * {@link IRootBlockView#getCommitCounter() commit counter} is chosen by * default. *

* Note: For historical compatibility, rootBlock1 is chosen if * both root blocks have the same {@link IRootBlockView#getCommitCounter()}. * * @param rootBlock0 * Root block 0 (may be null if this root block is * bad). * @param rootBlock1 * Root block 1 (may be null if this root block is * bad). * @param alternateRootBlock * When true, the alternate root block will be * chosen. This flag only makes sense when you have two root * blocks to choose from and you want to choose the other one. * @param ignoreBadRootBlock * When true, a bad root block will be ignored. * * @return The chosen root block. * * @throws RuntimeException * if no root block satisfies the criteria. */ public static IRootBlockView chooseRootBlock( final IRootBlockView rootBlock0, final IRootBlockView rootBlock1, final boolean alternateRootBlock,final boolean ignoreBadRootBlock) { final IRootBlockView rootBlock; if (!ignoreBadRootBlock && (rootBlock0 == null || rootBlock1 == null)) { /* * Do not permit the application to continue with a damaged * root block. */ throw new RuntimeException( "Bad root block(s): rootBlock0 is " + (rootBlock0 == null ? "bad" : "ok") + ", rootBlock1=" + (rootBlock1 == null ? "bad" : "ok")); } if (alternateRootBlock) { /* * A request was made to use the alternative root block. */ if (rootBlock0 == null || rootBlock1 == null) { /* * Note: The [alternateRootBlock] flag only makes sense when you * have two root blocks to choose from and you want to choose * the other one. */ throw new RuntimeException( "Can not use alternative root block since one root block is damaged."); } else { log.warn("Using alternate root block"); } } /* * Choose the root block based on the commit counter. * * Note: The commit counters MAY be equal. This will happen if * we rollback the journal and override the current root block * with the alternate root block. It is also true when we first * create a Journal. * * Note: If either root block was damaged then that rootBlock * reference will be null and we will use the other rootBlock * reference automatically. (The case where both root blocks are * bad is trapped above.) */ final long cc0 = rootBlock0 == null ? -1L : rootBlock0 .getCommitCounter(); final long cc1 = rootBlock1 == null ? -1L : rootBlock1 .getCommitCounter(); if (rootBlock0 == null) { // No choice. The other root block does not exist. rootBlock = rootBlock1; } else if (rootBlock1 == null) { // No choice. The other root block does not exist. rootBlock = rootBlock0; } else { /* * A choice exists, compare the commit counters. * * Note: As a historical artifact, this logic will choose * [rootBlock1] when the two root blocks have the same * [commitCounter]. With the introduction of HA support, code in * AbstractJournal#setQuorumToken() now depends on this choice * policy to decide which root block it will take as the current * root block. */ rootBlock = (cc0 > cc1 // ? (alternateRootBlock ? rootBlock1 : rootBlock0) // : (alternateRootBlock ? rootBlock0 : rootBlock1)// ); } if (log.isInfoEnabled()) log.info("chosenRoot: " + rootBlock); return rootBlock; } /** * Generate the root blocks. They are for all practical purposes identical. * * @param bufferMode * @param offsetBits * @param createTime * @param quorumToken * @param storeUUID */ public RootBlockUtility( final BufferMode bufferMode, final int offsetBits, final long createTime, final long quorumToken, final UUID storeUUID ) { if (bufferMode == null) throw new IllegalArgumentException("BufferMode is required."); if (createTime == 0L) throw new IllegalArgumentException("Create time may not be zero."); if (storeUUID == null) throw new IllegalArgumentException("Store UUID is required."); final ChecksumUtility checker = ChecksumUtility.threadChk.get(); /* * WORM: The offset at which the first record will be written. This is * zero(0) since the buffer offset (0) is the first byte after the root * blocks. * * RWStore: The field is ignored. The RWStore skips over a block to have * a good byte alignment on the file. */ final long nextOffset = 0L; final long closeTime = 0L; final long commitCounter = 0L; final long firstCommitTime = 0L; final long lastCommitTime = 0L; final long commitRecordAddr = 0L; final long commitRecordIndexAddr = 0L; final StoreTypeEnum stenum = bufferMode.getStoreType(); final long blockSequence = IRootBlockView.NO_BLOCK_SEQUENCE; rootBlock0 = new RootBlockView(true, offsetBits, nextOffset, firstCommitTime, lastCommitTime, commitCounter, commitRecordAddr, commitRecordIndexAddr, storeUUID, // blockSequence, quorumToken,// 0L, 0L, stenum, createTime, closeTime, RootBlockView.currentVersion, checker); rootBlock1 = new RootBlockView(false, offsetBits, nextOffset, firstCommitTime, lastCommitTime, commitCounter, commitRecordAddr, commitRecordIndexAddr, storeUUID, // blockSequence, quorumToken,// 0L, 0L, stenum, createTime, closeTime, RootBlockView.currentVersion, checker); this.rootBlock = rootBlock0; } /** * Dumps the root blocks for the specified file. * * @param args * filename * * @throws IOException */ public static void main(final String[] args) throws IOException { if (args.length == 0) { System.err.println("usage: "); System.exit(1); } final File file = new File(args[0]); if (!file.exists()) { System.err.println("Not found: " + file); System.exit(1); } /** * Used to re-open the {@link FileChannel} in this class. */ final IReopenChannel opener = new IReopenChannel() { // read-only. static private final String fileMode = "r"; private RandomAccessFile raf = null; public String toString() { return file.toString(); } public FileChannel reopenChannel() throws IOException { if (raf != null && raf.getChannel().isOpen()) { /* * The channel is still open. If you are allowing concurrent reads * on the channel, then this could indicate that two readers each * found the channel closed and that one was able to re-open the * channel before the other such that the channel was open again by * the time the 2nd reader got here. */ return raf.getChannel(); } // open the file. this.raf = new RandomAccessFile(file, fileMode); return raf.getChannel(); } }; // validate the root blocks using their checksums. final boolean validateChecksum = true; // option is ignored since we are not not opening the Journal. final boolean alternateRootBlock = false; // Open even if one root block is bad. final boolean ignoreBadRootBlock = true; final RootBlockUtility u = new RootBlockUtility(opener, file, validateChecksum, alternateRootBlock, ignoreBadRootBlock); System.out.println("rootBlock0: " + u.rootBlock0); System.out.println("rootBlock1: " + u.rootBlock1); System.out.println("Current root block is rootBlock" + (u.rootBlock == u.rootBlock0 ? "0" : "1")); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy