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

com.bigdata.journal.DiskBackedBufferStrategy 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
*/
package com.bigdata.journal;

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

import com.bigdata.io.FileChannelUtility;
import com.bigdata.io.IReopenChannel;
import com.bigdata.rawstore.IAllocationContext;
import com.bigdata.rawstore.IPSOutputStream;

/**
 * Abstract base class for implementations that use a direct buffer as a write
 * through cache to an image on the disk. This covers both the
 * {@link BufferMode#Direct}, where we use explicit IO operations, and the
 * {@link BufferMode#Mapped}, where we memory-map the image. Common features
 * shared by these implementations deal mainly with initialization of a new disk
 * image.
 * 
 * @todo write tests of the disk-only mode operations when overflowing an int32
 *       extent.
 * 
 * @todo consider a read buffer that uses a weak value cache backed by an LRU
 *       hard reference cache, which in turn evicts buffers back to a pool for
 *       reuse. The pool would bin buffers by size, keep no more than some #of
 *       buffers of a given size, and offer a service to allocate buffers of no
 *       less than a given capacity. This might reduce heap churn by recycling
 *       buffers in addition to providing minimizing the need to hit the OS disk
 *       cache, the disk buffers, or the disk platter.
 * 
 * @author Bryan Thompson
 * @version $Id$
 */
abstract public class DiskBackedBufferStrategy extends BasicBufferStrategy
        implements IDiskBasedStrategy {

    /**
     * The name of the backing file.
     */
    final File file;

    /**
     * The mode used to open the backing file.
     */
    final String fileMode;
    
    /**
     * Interface for random access on the backing file.
     */
    /*final*/ RandomAccessFile raf;

    final public int getHeaderSize() {
        
        return headerSize;
        
    }

    final public File getFile() {
        
        return file;
        
    }
    
    final public RandomAccessFile getRandomAccessFile() {
        
        return raf;
        
    }
    
    final public FileChannel getChannel() {

        return getRandomAccessFile().getChannel();
        
    }
    
    final public boolean isStable() {
        
        return true;
        
    }

    public void delete(long addr) {
    	// void
    }
    
    /**
     * Forces the data to disk.
     */
    public void force(final boolean metadata) {
        
        try {

            getChannel().force( metadata );
            
        } catch( IOException ex ) {
            
            throw new RuntimeException(ex);
            
        }           
        
    }
    
    /**
     * Closes the file.
     */
    public void close() {

        /*
         * Note: this clears the [open] flag. It is important to do this first
         * so that we do not re-open the channel once it has been closed.
         */

        super.close();

        try {

//            FileLockUtility.closeFile(file, raf);
            synchronized (this) {
                if (raf != null && raf.getChannel().isOpen()) {
                    raf.close();
                }
            }
            
        } catch( IOException ex ) {
            
            throw new RuntimeException(ex);
            
        }
        
    }

    public void deleteResources() {

        if (isOpen())
            throw new IllegalStateException();

        if (file.exists() && !file.delete()) {

            log.warn("Could not delete file: " + file.getAbsoluteFile());

        }

    }

    DiskBackedBufferStrategy(long maximumExtent, BufferMode bufferMode,
            FileMetadata fileMetadata) {

        super(maximumExtent, fileMetadata.offsetBits, fileMetadata.nextOffset,
                FileMetadata.headerSize0, fileMetadata.extent, bufferMode,
                fileMetadata.buffer, fileMetadata.readOnly);

        this.file = fileMetadata.file;
        
        this.fileMode = fileMetadata.fileMode;
        
        this.raf = fileMetadata.raf;
        
    }
   
    public ByteBuffer readRootBlock(final boolean rootBlock0) {
        
        if(!isOpen()) throw new IllegalStateException();

        final ByteBuffer tmp = ByteBuffer
                .allocate(RootBlockView.SIZEOF_ROOT_BLOCK);

        try {

            FileChannelUtility.readAll(opener, tmp,
                    rootBlock0 ? FileMetadata.OFFSET_ROOT_BLOCK0
                            : FileMetadata.OFFSET_ROOT_BLOCK1);

        } catch (IOException ex) {

            throw new RuntimeException(ex);

        }

        tmp.position(0); // resets the position.

        return tmp;

    }
    
    public void writeRootBlock(IRootBlockView rootBlock,ForceEnum forceOnCommit) {

        if (rootBlock == null)
            throw new IllegalArgumentException();

        try {

            final ByteBuffer data = rootBlock.asReadOnlyBuffer();
            
            final long pos = rootBlock.isRootBlock0() ? FileMetadata.OFFSET_ROOT_BLOCK0
                    : FileMetadata.OFFSET_ROOT_BLOCK1;
            
            FileChannelUtility.writeAll(opener, data, pos);

            if( forceOnCommit != ForceEnum.No ) {

                force(forceOnCommit == ForceEnum.ForceMetadata);
            
            }

        }

        catch (IOException ex) {

            throw new RuntimeException(ex);

        }

    }

    synchronized public long transferTo(RandomAccessFile out)
            throws IOException {
        
        return super.transferFromDiskTo(this, out);
        
    }

    /**
     * The backing file is fully buffered so it does not need to be open for a
     * read to succeed. However, we use this as an opportunity to transparently
     * re-open the {@link FileChannel} if it has been closed asynchronously or
     * in response to an interrupt (that is, if we discover that the channel is
     * closed but {@link #isOpen()} still returns true).
     */
    public ByteBuffer read(final long addr) {
        
        if(isOpen() && !raf.getChannel().isOpen()) {
            
            try {
                reopenChannel();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            
        }
        
        return super.read(addr);
        
    }
        
//    /**
//     * This method transparently re-opens the channel for the backing file.
//     * 

// * Note: This method is synchronized so that concurrent readers do not try // * to all open the store at the same time. // *

// * Note: This method is ONLY invoked by readers. This helps to ensure that a // * writer that has been interrupted can not regain access to the channel (it // * does not prevent it, but re-opening for writers is asking for trouble). // * // * @return true iff the channel was re-opened. // * // * @throws IllegalStateException // * if the buffer strategy has been closed. // * // * @throws RuntimeException // * if the backing file can not be opened (can not be found or // * can not acquire a lock). // */ // synchronized private boolean reopenChannel() { // // if(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 true; // // } // // if(!isOpen()) { // // // the buffer strategy has been closed. // // return false; // // } // // try { // // raf = FileLockUtility.openFile(file, fileMode, true/*tryFileLock*/); // // log.warn("Re-opened file: "+file); // // } catch(IOException ex) { // // throw new RuntimeException(ex); // // } // // return true; // // } /** * Used to re-open the {@link FileChannel} in this class. */ protected final IReopenChannel opener = new IReopenChannel() { public String toString() { return file.toString(); } public FileChannel reopenChannel() throws IOException { return DiskBackedBufferStrategy.this.reopenChannel(); } }; /** * This method transparently re-opens the channel for the backing file. * * @throws IllegalStateException * if the store is closed. * * @throws IOException * if the backing file can not be locked. */ final synchronized private FileChannel reopenChannel() throws IOException { assertOpen(); 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); if (log.isInfoEnabled()) log.info("(Re-)opened file: " + file); if (bufferMode != BufferMode.Mapped) { try { /* * Request a shared file lock. */ final boolean readOnly = "r".equals(fileMode); if (raf.getChannel() .tryLock(0, Long.MAX_VALUE, readOnly/* shared */) == null) { /* * Note: A null return indicates that someone else holds the * lock. This can happen if the platform does not support * shared locks or if someone requested an exclusive file * lock. */ try { raf.close(); } catch (Throwable t) { // ignore. } throw new IOException("File already locked? file=" + file); } } catch (IOException ex) { /* * Note: This is true of NFS volumes. This is Ok and should be * ignored. However the backing file is not protected against * accidental deletes or overwrites. */ if (log.isInfoEnabled()) log.info("FileLock not supported: file=" + file, ex); } } return raf.getChannel(); } /** * Protocol support for HAWrite */ public void setNextOffset(long lastOffset) { // void for default DiskBackedBufferStrategy } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy