Logger1.java :  » Database-DBMS » Quadcap-Embeddable-Database » com » quadcap » sql » file » Java Open Source

Java Open Source » Database DBMS » Quadcap Embeddable Database 
Quadcap Embeddable Database » com » quadcap » sql » file » Logger1.java
package com.quadcap.sql.file;

/* Copyright 1999 - 2003 Quadcap Software.  All rights reserved.
 *
 * This software is distributed under the Quadcap Free Software License.
 * This software may be used or modified for any purpose, personal or
 * commercial.  Open Source redistributions are permitted.  Commercial
 * redistribution of larger works derived from, or works which bundle
 * this software requires a "Commercial Redistribution License"; see
 * http://www.quadcap.com/purchase.
 *
 * Redistributions qualify as "Open Source" under  one of the following terms:
 *   
 *    Redistributions are made at no charge beyond the reasonable cost of
 *    materials and delivery.
 *
 *    Redistributions are accompanied by a copy of the Source Code or by an
 *    irrevocable offer to provide a copy of the Source Code for up to three
 *    years at the cost of materials and delivery.  Such redistributions
 *    must allow further use, modification, and redistribution of the Source
 *    Code under substantially the same terms as this license.
 *
 * Redistributions of source code must retain the copyright notices as they
 * appear in each source code file, these license terms, and the
 * disclaimer/limitation of liability set forth as paragraph 6 below.
 *
 * Redistributions in binary form must reproduce this Copyright Notice,
 * these license terms, and the disclaimer/limitation of liability set
 * forth as paragraph 6 below, in the documentation and/or other materials
 * provided with the distribution.
 *
 * The Software is provided on an "AS IS" basis.  No warranty is
 * provided that the Software is free of defects, or fit for a
 * particular purpose.  
 *
 * Limitation of Liability. Quadcap Software shall not be liable
 * for any damages suffered by the Licensee or any third party resulting
 * from use of the Software.
 */

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import java.util.Properties;

import com.quadcap.sql.io.ObjectOutputStream;
import com.quadcap.sql.io.ObjectInputStream;

import com.quadcap.util.collections.LongMap;

import com.quadcap.util.Debug;
import com.quadcap.util.Util;

/**
 * A Logger implmeentation using a fixed size circular buffer.
 *
 * @author Stan Bailes
 */
public class Logger1 implements Logger {
    LogBuffer                           cb;
    InputStream                         cbIn = null;
    ObjectOutputStream                  oos = null;
    ByteArrayRandomAccess               bra = new ByteArrayRandomAccess();
    RandomAccessInputStream             ris = new RandomAccessInputStream(bra);
    ObjectInputStream                   ois = new ObjectInputStream(ris);
    byte[]                              tmp = new byte[8];

    /**
     * Transaction map:
     *   - ordered by transactionStart
     *   - first transaction is the oldest transaction still running
     *        so when committing the oldest transaction, move the begin
     *   - last transaction is the newest transaction still running
     * transactions!
     */
    TransMap[]                          trans = new TransMap[16];

    /**
     * Keeps track of the number of TransMap entries allocated.  Some entries
     * will correspond to completed but not yet checkpointed transactions,
     * so this number may be larger than the number of actual live transactions.
     */
    int                                 numTrans;

    /**
     * Keep the actual count of live transactions as determined by calls
     * to beginTransaction, endTransaction.
     */
    int                                 liveTrans       = 0;

    /**
     * The starting position of the most recent op we wrote to the file
     */
    long                                prevOp;

    int                                 maxSize = 128 * 1024 * 1024;
    int                                 minSize = 128 * 1024;

    Log                                 myLog;
    Datafile                            db;

    Logger1() {}

    public void init(Log log, boolean create, Properties props)
        throws IOException
    {
        this.myLog = log;
        this.db = log.getDatafile();
        this.cb = new LogBuffer();
        // XXX better would be a call to Datafile.getProperty("logfile")
        /*{com.quadcap.sql.Datafile-conn.xml-29}
         * <config-var>
         *   <config-name>maxLogSize</config-name>
         *   <config-dflt>128 M bytes</config-dflt>
         *   <config-desc>For loggers which support rollback, the maximum
         *     size of the rollback log.  Once this maximum is reached, it
         *     may be necessary to abort the oldest transaction and checkpoint
         *     in an attempt to regain log space.</config-desc>
         * </config-var>
         */
        maxSize = Integer.parseInt(props.getProperty("maxLogSize", "" + maxSize));

        /*{com.quadcap.sql.Datafile-conn.xml-29}
         * <config-var>
         *   <config-name>minLogSize</config-name>
         *   <config-dflt>128 K bytes</config-dflt>
         *   <config-desc>For loggers which support rollback, the minimum size
         *     at which a logging checkpoint operation will occur.  The logging
         *     checkpoint cleans the log of entries which correspond to completed
         *     transactions.</config-desc>
         * </config-var>
         */
        minSize = Integer.parseInt(props.getProperty("minLogSize", "" + minSize));

        File logfile = new File(db.getDbRootDir(), "logfile");
        RandomAccessFile raf = new RandomAccessFile(logfile, "rw");
        FileRandomAccess fra = new FileRandomAccess(raf, maxSize);
        RandomAccess ra = fra;
        if (create) {
            cb.init(ra, maxSize);
        } else {
            lastSize = ra.size();
            cb.init(ra, props);
        }
    }

    public void init(File file) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        FileRandomAccess fra = new FileRandomAccess(raf, raf.length());
        RandomAccess ra = fra;
        this.cb = new LogBuffer();
        lastSize = ra.size();
        cb.init(ra, new Properties());
     }


    /**
     * Inner class used to to track active transactions.
     */
    class TransMap {
        long                    transId;
        /**
         * If >0, the position of the first op in the transaction.
         * This will generally be the BEGIN_TRANSACTION op.
         */
        int                     bufStart;
        /**
         * If >0, the position of the most recently written op in the
         * transaction.  
         */
        int                     bufEnd;
        boolean                 complete;
        
        public void init(long t, int p) {
            this.transId = t;
            this.bufStart = p;
            this.bufEnd = p;
            this.complete = false;
        }

        public void init(TransMap t) {
            this.transId = t.transId;
            this.bufStart = -1;
            this.bufEnd = -1;
            this.complete = t.complete;
        }

        public void copy(TransMap t) {
            this.transId = t.transId;
            this.bufStart = t.bufStart;
            this.bufEnd = t.bufEnd;
            this.complete = t.complete;
        }

        public String toString() {
            return "TransMap[" + transId + ": " + bufStart + "-" + bufEnd +
                (complete ? " (COMPLETE)" : "") + "]";
        }
    }

    public String toString() {
        StringBuffer sb = new StringBuffer("Logger1 {" + cb.getCheckpoint() +
                                           ", " + cb.getEnd() + "} ");
        for (int i = 0; i < trans.length; i++) {
            TransMap tm = trans[i];
            if (tm != null && !tm.complete) {
                sb.append(tm.toString());
            }
        }
        return sb.toString();
    }

    public int getCheckpoint() {
        return cb.getCheckpoint();
    }

    public int getEnd() {
        return cb.getEnd();
    }
    
    public int rput(LogEntry op) throws IOException {
        int pos = cb.getEnd();
        op.setPosition(pos);

        long id = op.getTransactionId();
        int tx = findTransaction(id);
        if (tx >= 0) {
            TransMap tm = trans[tx];
            op.setPrev(tm.bufEnd);
            tm.bufEnd = pos;
        }

        try {
            if (oos == null) {
                oos = new ObjectOutputStream(cb.getOutputStream());
            }
            oos.writeObject(op);
            oos.flush();
        } catch (IOException ex) {
            if (tx >= 0) {
                trans[tx].bufEnd = op.getPrev();
            }
            throw ex;
        }
        return pos;
    }
    
    public void put(LogEntry op) throws IOException {
        int pos = rput(op);
        switch (op.getCode()) {
        case LogEntry.BEGIN_TRANSACTION:
            beginTransaction(op.getTransactionId(), pos);
            break;
        case LogEntry.COMMIT:
            endTransaction(op.getTransactionId());
            break;
        }
        //Debug.println("Logger1.put(" + op + ") [" + pos +
        //              "] ----------------------");
    }
    
    public void setRedoState(LogEntry op, int state) throws IOException {
        int pos = op.getPosition();
        //Debug.println("setRedoState(" + op + ", " + state + ")"); 
        cb.writeByte(pos+1, (byte)state);
        op.redoState = state;
    }
    
    public LogEntry getLastOp(long transId) throws IOException {
        LogEntry ret = null;
        int tx = findTransaction(transId);
        if (tx >= 0) {
            TransMap tm = trans[tx];
            ret = readEntry(tm.bufEnd);
        }
        //#ifdef DEBUG
        if (ret == null) {
            Debug.println(Util.stackTrace());
            Debug.println("getLastOp(" + transId + ") = null, trans:");
            for (int i = 0; i < numTrans; i++) {
                Debug.println("trans[" + i + "] = " + trans[i]);
            }
        }
        //#endif
        return ret;
    }

    public LogEntry getPrevOp(LogEntry op) throws IOException {
        int pos = op.getPrev();
        int len = op.getPosition() - pos;
        LogEntry ret = readEntry(pos, len);
        return ret;        
    }

    public LogEntry getFirstOp() throws IOException {
        LogEntry ret = readEntry(cb.getBegin());
        return ret;
    }

    public LogEntry getNextOp() throws IOException {
        return readEntry();
    }

    public void sync() throws IOException {
        cb.sync();
    }

    // close all the files
    public void close() throws IOException {
        cb.close();
    }

    long lastSize = 0;
    public void reset() throws IOException {        
        cb.reset();
        if (lastSize - cb.size() > 100 * 1000) {
            cb.truncate();
        }
        lastSize = cb.size();
    }

    public long getOldestTransaction() {
        if (numTrans > 0) {
            return trans[0].transId;
        } else {
            return -1;
        }
    }

    public int getActiveTransactionCount() {
        return liveTrans;
    }

    public LongMap getActiveTransactions() {
        LongMap map = new LongMap(16);
        for (int i = 0; i < numTrans; i++) {
            TransMap t = trans[i];
            if (!t.complete) {
                map.put(t.transId, "");
            }
        }
        return map;
    }

    public void checkpoint() throws IOException {
        int offset = 0;
        for (int i = 0; i < numTrans; i++) {
            TransMap t = trans[i];
            if (t.complete) {
                offset++;
            } else if (offset > 0) {
                trans[i-offset].init(t);
            }
        }
        //#ifdef DEBUG
        if (Trace.bit(22)) {
            Debug.println("BEGIN checkpoint [" + cb.getBegin() + "-" +
                          cb.getEnd() + " (" + numTrans + " transactions)]");
        }
        //#endif
        numTrans -= offset;
        //Debug.println("Checkpoint: -" + offset + " = " + numTrans);
        LogEntry entry = readEntry(0);
        cb.reset();
        if (numTrans > 0) {
            while (entry != null) {
                boolean keep = false;
                long id = entry.getTransactionId();
                int tx = findTransaction(id);
                if (tx >= 0) {
                    TransMap tm = trans[tx];
                    keep = !tm.complete;
                    if (keep) {
                        if (tm.bufStart < 0) tm.bufStart = cb.getEnd();
                    }
                }
                if (keep) {
                    //#ifdef DEBUG
                    if (Trace.bit(23)) {
                        Debug.println("   [" + cb.getEnd() + "] -> " + entry);
                    }
                    //#endif
                    rput(entry);
                } else {
                    //#ifdef DEBUG
                    if (Trace.bit(23)) {
                        Debug.println("   [" + cb.getEnd() + "] -> DISCARD: " + entry);
                    }
                    //#endif
                    entry.discard(db);
                }
                entry = readEntry();
            }
        }
        cb.checkpoint();
        //#ifdef DEBUG
        if (Trace.bit(22)) {
            Debug.println("END checkpoint [" + cb.getBegin() + "-" + cb.getEnd() +
                          " (" + numTrans + " transactions)]");
        }
        //#endif
    }

    //--------------------------------- private stuff
    
    private final void beginTransaction(long transId, int pos) {
        if (numTrans >= trans.length) {
            int newcap  = numTrans + (numTrans/2) + 2;
            trans = (TransMap[])Util.checkCapacity(trans, newcap);
        }
        TransMap t = trans[numTrans];
        if (t == null) {
            t = new TransMap();
            trans[numTrans] = t;
        }

        // A race condition exists -- this might *not* be the newest trans,
        // and we want to keep the 'trans' array ordered properly, so search
        // backwards from the end to make sure we put this fellow in the
        // right place.
        for (int i = numTrans-1; i >= 0; i--) {
            if (trans[i].transId > transId) {
                trans[i+1].copy(trans[i]);
                t = trans[i];
            } else {
                break;
            }
        }
        numTrans++;
        //Debug.println("Begin T:" + transId + " = " + numTrans);
        liveTrans++;
        t.init(transId, pos);
        //Debug.println("beginTransaction(" + transId + "): " + numTrans);
    }

    private final void endTransaction(final long transId) throws IOException {
        //Debug.println("endTransaction(" + transId + "): " + numTrans);
        int pos = findTransaction(transId);
        if (pos >= 0) {
            // Mark the transaction as complete.  We don't decrement
            // numTrans here; we wait until the next checkpoint, where
            // all of the completed transactions can be reclaimed at
            // once.
            trans[pos].complete = true;
            liveTrans--;
            if (pos == 0 && cb.size() > minSize) {
                checkpoint();
            }
        }
    }

    /**
     * Find the TransMap entry for the given transaction id
     */
    int findTransaction(long transId) {
        int lo = 0;
        int hi = numTrans-1;
        while (hi >= lo) {
            int mid = (hi+lo) / 2;
            TransMap t = trans[mid];
            if (t.transId == transId) {
                return mid;
            }
            if (t.transId < transId) {
                lo = mid+1;
            } else {
                hi = mid-1;
            }
        }
        return -1;
    }

    private final LogEntry readEntry(int pos, int len) throws IOException {
        if (pos < 0) return null;
        bra.resize(len);
        byte[] buf = bra.getBytes();
        cb.read(pos, buf, 0, len);
        ris.setPosition(0);
        ois.setInputStream(ris);
        ois.setPosition(pos);
        
        LogEntry ret = readEntry();
        return ret;
    }

    private final LogEntry readEntry(int pos) throws IOException {
        if (pos < 0) return null;
        cbIn = new BufferedInputStream(cb.getInputStream(pos));
        ois.setInputStream(cbIn);
        ois.setPosition(pos);

        LogEntry ret = readEntry();
        return ret;
    }

    private final LogEntry readEntry() throws IOException {
        Object obj = null;
        try {
            int pos = (int)ois.getPosition();
            LogEntry ret = (LogEntry)(obj = ois.readObject());
            if (ret != null) {
                ret.setPosition(pos);
            }
            return ret;
        } catch (EOFException ex) {
            return null;
        } catch (ClassCastException ez) {
            throw new DatafileException(ez);
        } catch (ClassNotFoundException ex) {
            throw new DatafileException(ex);
        }
    }
}
    
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.