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

com.bigdata.journal.SnapshotTask 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.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.log4j.Logger;

import com.bigdata.journal.AbstractJournal.ISnapshotData;
import com.bigdata.quorum.Quorum;

/**
 * Take a snapshot of the journal.
 * 
 * @author bryan
 * 
 * @see  Snapshot mechanism breaks
 *      with metabit demi-spaces 
 * @see  Online backup for Journal
 *      
 */
public class SnapshotTask implements Callable {

   private final Journal journal;
   private final ISnapshotFactory snapshotFactory;
   
   protected static final Logger log = Logger.getLogger(SnapshotTask.class);
   
   public interface Options {
	   
	  /**
	   * 
	   * Java property to override the default GZIP buffer size used for {@link GZipInputStream} and {@link GZipOutputStream}. 
	   * 
	   * This specifies the size in Bytes to use.  The default is 512k.
	   * 
	   * -Dcom.bigdata.journal.SnapshotTask.gzipBufferSize=512
	   * 
	   * A larger value such as below is recommended for larger files.
	   * 
	   * -Dcom.bigdata.journal.SnapshotTask.gzipBufferSize=65535
	   *  
	   */
	   
	   public static final String GZIP_BUFFER_SIZE = SnapshotTask.class.getClass().getName()+".gzipBufferSize"; 
	   
   }
   
   private static int getGzipBuffer() {
	   
	   final String s = System.getProperty(Options.GZIP_BUFFER_SIZE);
	   
	   if(s == null || s.isEmpty()) {
		   return DEFAULT_BUFFER;
	   } else {
		   return Integer.parseInt(s);
	   }
	   
   }
   
   /**
    * See BLZG-1732
    */
   private static final int GZIP_BUFFER =  getGzipBuffer();
   private static final int DEFAULT_BUFFER = 512;
   
   /**
    * The prefix for the temporary files used to generate snapshots.
    */
   public final static String SNAPSHOT_TMP_PREFIX = "snapshot";
   
   /**
    * The suffix for the temporary files used to generate snapshots.
    */
   public final static String SNAPSHOT_TMP_SUFFIX = ".tmp";

   public SnapshotTask(final Journal journal,
         final ISnapshotFactory snapshotFactory) {
      if (journal == null)
         throw new IllegalArgumentException();
      if (snapshotFactory == null)
         throw new IllegalArgumentException();
      this.journal = journal;
      this.snapshotFactory = snapshotFactory;
   }

   @Override
   public ISnapshotResult call() throws Exception {

      // Grab a read lock.
      final long txId = journal.newTx(ITx.READ_COMMITTED);
      try {

         /*
          * Get all snapshot core data, including rootblocks and any allocation
          * data, setting the current committed rootblock view.
          */
         final AtomicReference rbv = new AtomicReference();
         final ISnapshotData coreData = journal.snapshotAllocationData(rbv);

         if (rbv.get().getCommitCounter() == 0L) {

            throw new IllegalStateException("Journal is empty");

         }

         final File file = snapshotFactory.getSnapshotFile(rbv.get());

         if (file.exists() && file.length() != 0L) {

            /*
             * Snapshot exists and is not (logically) empty.
             * 
             * Note: The SnapshotManager will not recommend taking a snapshot if
             * a snapshot already exists for the current commit point since
             * there is no committed delta that can be captured by the snapshot.
             * 
             * This code makes sure that we do not attempt to overwrite a
             * snapshot if we already have one for the same commit point. If you
             * want to re-generate a snapshot for the same commit point (e.g.,
             * because the existing one is corrupt) then you MUST remove the
             * pre-existing snapshot first.
             */

            throw new IOException("File exists: " + file);

         }

         final File parentDir = file.getParentFile();

         // Make sure the parent directory(ies) exist.
         if (!parentDir.exists())
            if (!parentDir.mkdirs())
               throw new IOException("Could not create directory: " + parentDir);

         /*
          * Create a temporary file. We will write the snapshot here. The file
          * will be renamed onto the target file name iff the snapshot is
          * successfully written.
          */
         final File tmp = File.createTempFile(
               SNAPSHOT_TMP_PREFIX,
               SNAPSHOT_TMP_SUFFIX, parentDir);

         OutputStream osx = null;
         DataOutputStream os = null;
         boolean success = false;
         try {

            osx = new FileOutputStream(tmp);

            if (snapshotFactory.getCompress())
               osx = new GZIPOutputStream(osx, GZIP_BUFFER);

            os = new DataOutputStream(osx);

            // write out the file data.
            ((IHABufferStrategy) journal.getBufferStrategy()).writeOnStream(os,
                  coreData, null/* quorum */, Quorum.NO_QUORUM);

            // flush the output stream.
            os.flush();

            // done.
            success = true;
         } catch (Throwable t) {
            /*
             * Log @ ERROR and launder throwable.
             */
            Journal.log.error(t, t);
            if (t instanceof Exception)
               throw (Exception) t;
            else
               throw new RuntimeException(t);
         } finally {

            if (os != null) {
               try {
                  os.close();
               } finally {
                  // ignore.
                  os = null;
                  osx = null;
               }
            } else if (osx != null) {
               try {
                  osx.close();
               } finally {// ignore
                  osx = null;
               }
            }

            /*
             * Either rename the temporary file onto the target filename or
             * delete the tempoary file. The snapshot is not considered to be
             * valid until it is found under the appropriate name.
             */
            if (success) {

               // if (!journal.getQuorum().getClient().isJoinedMember(token)) {
               // // Verify before putting down the root blocks.
               // throw new QuorumException(
               // "Snapshot aborted: service not joined with met quorum.");
               // }

               if (!tmp.renameTo(file)) {

                  Journal.log.error("Could not rename " + tmp + " as " + file);

               } else {

                  if (Journal.log.isInfoEnabled())
                     Journal.log.info("Captured snapshot: " + file
                           + ", commitCounter=" + rbv.get().getCommitCounter()
                           + ", length=" + file.length());

               }

            } else {

               if (!tmp.delete()) {

                  Journal.log.warn("Could not delete temporary file: " + tmp);

               }

            }

         }

         // Done.
         return new SnapshotResult(file, snapshotFactory.getCompress(),
               rbv.get());
   
      } finally {
         // Release the read lock.
         journal.abort(txId);
      }
   }
   
   /**
    * Copy the input stream to the output stream.
    * 
    * @param content
    *            The input stream.
    * @param outstr
    *            The output stream.
    * 
    * @throws IOException
    */
   static private void copyStream(final InputStream content,
           final OutputStream outstr) throws IOException {

       final byte[] buf = new byte[GZIP_BUFFER];

       while (true) {

           final int rdlen = content.read(buf);

           if (rdlen <= 0) {

               break;

           }

           outstr.write(buf, 0, rdlen);

       }

   }
   
   /**
    * Decompress a snapshot onto the specified file. The original file is not
    * modified.
    * 
    * @param src
    *            The snapshot.
    * @param dst
    *            The file onto which the decompressed snapshot will be written.
    * 
    * @throws IOException
    *             if the source file does not exist.
    * @throws IOException
    *             if the destination file exists and is not empty.
    * @throws IOException
    *             if there is a problem decompressing the source file onto the
    *             destination file.
    */
   public static void decompress(final File src, final File dst)
           throws IOException {

       if (!src.exists())
           throw new FileNotFoundException(src.getAbsolutePath());

       if (dst.exists() && dst.length() != 0)
           throw new IOException("Output file exists and is not empty: "
                   + dst.getAbsolutePath());

       if (log.isInfoEnabled())
           log.info("src=" + src + ", dst=" + dst);

       InputStream is = null;
       OutputStream os = null;
       try {
    	   //BLZG-1732:   
           is = new GZIPInputStream(new FileInputStream(src), GZIP_BUFFER);
           os = new BufferedOutputStream(new FileOutputStream(dst));
           copyStream(is, os);
           os.flush();
       } finally {
           if (is != null)
               try {
                   is.close();
               } catch (IOException ex) {
               }
           if (os != null)
               try {
                   os.close();
               } catch (IOException ex) {
               }
       }

   }
   
} // class SnapshotTask




© 2015 - 2025 Weber Informatics LLC | Privacy Policy