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

jdbm.recman.TransactionManager Maven / Gradle / Ivy

The newest version!
/**
 * JDBM LICENSE v1.00
 *
 * Redistribution and use of this software and associated documentation
 * ("Software"), with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * 1. Redistributions of source code must retain copyright
 *    statements and notices.  Redistributions must also contain a
 *    copy of this document.
 *
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. The name "JDBM" must not be used to endorse or promote
 *    products derived from this Software without prior written
 *    permission of Cees de Groot.  For written permission,
 *    please contact [email protected].
 *
 * 4. Products derived from this Software may not be called "JDBM"
 *    nor may "JDBM" appear in their names without prior written
 *    permission of Cees de Groot.
 *
 * 5. Due credit should be given to the JDBM Project
 *    (http://jdbm.sourceforge.net/).
 *
 * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
 * Contributions are Copyright (C) 2000 by their associated contributors.
 *
 * $Id: TransactionManager.java,v 1.7 2005/06/25 23:12:32 doomdark Exp $
 */

package jdbm.recman;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

import jdbm.I18n;


/**
 *  This class manages the transaction log that belongs to every
 *  {@link RecordFile}. The transaction log is either clean, or
 *  in progress. In the latter case, the transaction manager
 *  takes care of a roll forward.
 *

* Implementation note: this is a proof-of-concept implementation * which hasn't been optimized for speed. For instance, all sorts * of streams are created for every transaction. */ // TODO: Handle the case where we are recovering lg9 and lg0, were we // should start with lg9 instead of lg0! public final class TransactionManager { private RecordFile owner; // streams for transaction log. private FileOutputStream fos; private ObjectOutputStream oos; /** * By default, we keep 10 transactions in the log file before * synchronizing it with the main database file. */ static final int DEFAULT_TXNS_IN_LOG = 10; /** * Maximum number of transactions before the log file is * synchronized with the main database file. */ private int _maxTxns = DEFAULT_TXNS_IN_LOG; /** * In-core copy of transactions. We could read everything back from * the log file, but the RecordFile needs to keep the dirty blocks in * core anyway, so we might as well point to them and spare us a lot * of hassle. */ private ArrayList[] txns = new ArrayList[DEFAULT_TXNS_IN_LOG]; private int curTxn = -1; /** Extension of a log file. */ static final String extension = ".lg"; /** * Instantiates a transaction manager instance. If recovery * needs to be performed, it is done. * * @param owner the RecordFile instance that owns this transaction mgr. */ TransactionManager( RecordFile owner ) throws IOException { this.owner = owner; recover(); open(); } /** * Synchronize log file data with the main database file. *

* After this call, the main database file is guaranteed to be * consistent and guaranteed to be the only file needed for * backup purposes. */ public void synchronizeLog() throws IOException { synchronizeLogFromMemory(); } /** * Set the maximum number of transactions to record in * the log (and keep in memory) before the log is * synchronized with the main database file. *

* This method must be called while there are no * pending transactions in the log. */ public void setMaximumTransactionsInLog( int maxTxns ) throws IOException { if ( maxTxns <= 0 ) { throw new IllegalArgumentException( I18n.err( I18n.ERR_563 ) ); } if ( curTxn != -1 ) { throw new IllegalStateException( I18n.err( I18n.ERR_564 ) ); } _maxTxns = maxTxns; txns = new ArrayList[maxTxns]; } /** Builds logfile name */ private String makeLogName() { return owner.getFileName() + extension; } /** Synchs in-core transactions to data file and opens a fresh log */ private void synchronizeLogFromMemory() throws IOException { close(); TreeSet blockList = new TreeSet( new BlockIoComparator() ); for ( int i = 0; i < _maxTxns; i++ ) { if ( txns[i] == null ) continue; // Add each block to the blockList, replacing the old copy of this // block if necessary, thus avoiding writing the same block twice for ( Iterator k = txns[i].iterator(); k.hasNext(); ) { BlockIo block = ( BlockIo ) k.next(); if ( blockList.contains( block ) ) { block.decrementTransactionCount(); } else { blockList.add( block ); } } txns[i] = null; } // Write the blocks from the blockList to disk synchronizeBlocks( blockList.iterator(), true ); owner.sync(); open(); } /** Opens the log file */ private void open() throws IOException { fos = new FileOutputStream( makeLogName() ); oos = new ObjectOutputStream( fos ); oos.writeShort( Magic.LOGFILE_HEADER ); oos.flush(); curTxn = -1; } /** Startup recovery on all files */ private void recover() throws IOException { String logName = makeLogName(); File logFile = new File( logName ); if ( !logFile.exists() ) return; if ( logFile.length() == 0 ) { logFile.delete(); return; } FileInputStream fis = new FileInputStream( logFile ); ObjectInputStream ois = new ObjectInputStream( fis ); try { if ( ois.readShort() != Magic.LOGFILE_HEADER ) { ois.close(); throw new Error( I18n.err( I18n.ERR_565 ) ); } } catch ( IOException e ) { // corrupted/empty logfile ois.close(); logFile.delete(); return; } while ( true ) { ArrayList blocks = null; try { blocks = ( ArrayList ) ois.readObject(); } catch ( ClassNotFoundException e ) { ois.close(); throw new Error( I18n.err( I18n.ERR_566, e ) ); } catch ( IOException e ) { // corrupted logfile, ignore rest of transactions break; } synchronizeBlocks( blocks.iterator(), false ); // ObjectInputStream must match exactly each // ObjectOutputStream created during writes try { ois = new ObjectInputStream( fis ); } catch ( IOException e ) { // corrupted logfile, ignore rest of transactions break; } } owner.sync(); ois.close(); logFile.delete(); } /** Synchronizes the indicated blocks with the owner. */ private void synchronizeBlocks( Iterator blockIterator, boolean fromCore ) throws IOException { // write block vector elements to the data file. while ( blockIterator.hasNext() ) { BlockIo cur = ( BlockIo ) blockIterator.next(); owner.synch( cur ); if ( fromCore ) { cur.decrementTransactionCount(); if ( !cur.isInTransaction() ) { owner.releaseFromTransaction( cur, true ); } } } } /** Set clean flag on the blocks. */ private void setClean( ArrayList blocks ) throws IOException { for ( Iterator k = blocks.iterator(); k.hasNext(); ) { BlockIo cur = ( BlockIo ) k.next(); cur.setClean(); } } /** Discards the indicated blocks and notify the owner. */ private void discardBlocks( ArrayList blocks ) throws IOException { for ( Iterator k = blocks.iterator(); k.hasNext(); ) { BlockIo cur = ( BlockIo ) k.next(); cur.decrementTransactionCount(); if ( !cur.isInTransaction() ) { owner.releaseFromTransaction( cur, false ); } } } /** * Starts a transaction. This can block if all slots have been filled * with full transactions, waiting for the synchronization thread to * clean out slots. */ void start() throws IOException { curTxn++; if ( curTxn == _maxTxns ) { synchronizeLogFromMemory(); curTxn = 0; } txns[curTxn] = new ArrayList(); } /** * Indicates the block is part of the transaction. */ void add( BlockIo block ) throws IOException { block.incrementTransactionCount(); txns[curTxn].add( block ); } /** * Commits the transaction to the log file. */ void commit() throws IOException { oos.writeObject( txns[curTxn] ); sync(); // set clean flag to indicate blocks have been written to log setClean( txns[curTxn] ); // reset ObjectOutputStream in order to store // newer states of BlockIo oos = new ObjectOutputStream( fos ); oos.reset(); } /** Flushes and syncs */ private void sync() throws IOException { oos.flush(); fos.flush(); fos.getFD().sync(); } /** * Shutdowns the transaction manager. Resynchronizes outstanding * logs. */ void shutdown() throws IOException { synchronizeLogFromMemory(); close(); } /** * Closes open files. */ private void close() throws IOException { sync(); oos.close(); fos.close(); oos = null; fos = null; } /** * Force closing the file without synchronizing pending transaction data. * Used for testing purposes only. */ void forceClose() throws IOException { oos.close(); fos.close(); oos = null; fos = null; } /** * Use the disk-based transaction log to synchronize the data file. * Outstanding memory logs are discarded because they are believed * to be inconsistent. */ void synchronizeLogFromDisk() throws IOException { close(); for ( int i = 0; i < _maxTxns; i++ ) { if ( txns[i] == null ) continue; discardBlocks( txns[i] ); txns[i] = null; } recover(); open(); } /** INNER CLASS. * Comparator class for use by the tree set used to store the blocks * to write for this transaction. The BlockIo objects are ordered by * their blockIds. */ public static class BlockIoComparator implements Comparator { public int compare( Object o1, Object o2 ) { BlockIo block1 = ( BlockIo ) o1; BlockIo block2 = ( BlockIo ) o2; int result = 0; if ( block1.getBlockId() == block2.getBlockId() ) { result = 0; } else if ( block1.getBlockId() < block2.getBlockId() ) { result = -1; } else { result = 1; } return result; } public boolean equals( Object obj ) { return super.equals( obj ); } } // class BlockIOComparator }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy