/** * */ package org.voltdb; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.io.SyncFailedException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.log4j.Logger; import org.voltdb.utils.DBBPool.BBContainer; import edu.brown.hstore.PartitionExecutor; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; public class AriesLogNative extends AriesLog { private static final Logger LOG = Logger.getLogger(AriesLogNative.class); private static final LoggerBoolean debug = new LoggerBoolean(); private static final LoggerBoolean trace = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug, trace); } boolean m_recoveryDone = false; boolean m_keepLogging = true; private boolean[] m_perSiteRecoveryDone; private long pointerToReplayLog; private long replayLogSize; RandomAccessFile ariesLogfile = null; private long totalLogSize; private long numTransactions; private long txnIdToBeginReplay; private static String m_logFileName ; private int m_numSites ; private int m_numPartitionsPerSite ; private static class LogDataWithAtom { public byte b[]; public AtomicBoolean isDurable; public LogDataWithAtom(byte b[], AtomicBoolean isDurable) { this.b = b; this.isDurable = isDurable; } } private List<LogDataWithAtom> m_waitingToFlush; private List<LogDataWithAtom> m_beingFlushed; public AriesLogNative(int numSites, int numPartitionsPerSite, String logFileName) { this(numSites, numPartitionsPerSite, 0, logFileName); // hardcode to 0 MB for now. } public AriesLogNative(int numSites, int numPartitionsPerSite, int size, String logFileName) { // Hardcode for now -- default value of 16 is also specified in // org.voltdb.compiler.DeploymentFileSchema.xsd this(numSites, numPartitionsPerSite, size, 16, logFileName); } public AriesLogNative(int numSites, int numPartitionsPerSite, int size, int syncFrequency, String logFileName) { //LOG.warn("AriesLogNative : numSites : "+numSites+ " logFileName : "+logFileName); m_perSiteRecoveryDone = new boolean[numSites*numPartitionsPerSite]; for (int i = 0; i < numSites*numPartitionsPerSite; i++) { m_perSiteRecoveryDone[i] = false; } fsyncFrequency = syncFrequency; logsize = size; m_waitingToFlush = new ArrayList<LogDataWithAtom>(); m_beingFlushed = null; isInitialized = false; totalLogSize = 0; numTransactions = 0; txnIdToBeginReplay = Long.MIN_VALUE; // initially set pointer to invalid value pointerToReplayLog = Long.MIN_VALUE; replayLogSize = 0; m_logFileName = logFileName; m_numSites = numSites; m_numPartitionsPerSite= numPartitionsPerSite; new Thread(this).start(); } public void setTxnIdToBeginReplay(long txnId) { if (txnId > 0) { txnIdToBeginReplay = txnId; } else { txnIdToBeginReplay = 1; } } public long getTxnIdToBeginReplay() { return txnIdToBeginReplay; } public boolean isReadyForReplay() { return (txnIdToBeginReplay > 0); } public void setPointerToReplayLog(long ariesReplayPointer, long size) { pointerToReplayLog = ariesReplayPointer; replayLogSize = size; } public long getPointerToReplayLog() { return pointerToReplayLog; } public long getReplayLogSize() { return replayLogSize; } @Override public synchronized void init() { if (!isInitialized) { try { long logsizeInMB = logsize; //XXX Disable this ariesLogfile = new RandomAccessFile(m_logFileName, "rw"); ariesLogfile.setLength(logsizeInMB * 1024 * 1024); ariesLogfile.seek(0); /* // XXX: DO NOT do this, its way too slow for a file several gigabytes in size. // Zero the log file just to be safe. for (long l = 0; l < ariesLogfile.length(); l++) { ariesLogfile.writeByte(0); } ariesLogfile.seek(0); */ } catch (FileNotFoundException e) { // TODO Auto-generated catch block //e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } isInitialized = true; } } private double getAverageLogSize() { if (numTransactions == 0) { return 0; } double avgLogSize = ((double) totalLogSize)/numTransactions; return avgLogSize; } public String getStatistics() { return String.valueOf(getAverageLogSize()); } private void flushData() { swapFlushListWithEmpty(); if (m_beingFlushed == null || m_beingFlushed.size() == 0) { if(m_beingFlushed == null){ //LOG.warn("AriesLogNative : m_beingFlushed :"+m_beingFlushed); } else{ //LOG.warn("AriesLogNative : m_beingFlushed size :"+m_beingFlushed.size()); } return; } // write all log data to the file // in memory for(LogDataWithAtom l : m_beingFlushed) { try { ariesLogfile.write(l.b); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // flush the log out try { ariesLogfile.getFD().sync(); LOG.debug("AriesLogNative : finished flushData :: File Size : "+ariesLogfile.length()); } catch (SyncFailedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(LogDataWithAtom l : m_beingFlushed) { l.isDurable.set(true); } m_beingFlushed = null; } /** * */ private void swapFlushListWithEmpty() { // Must synchronize to avoid insertions into the list // during the swap. synchronized (this) { m_beingFlushed = m_waitingToFlush; m_waitingToFlush = new ArrayList<LogDataWithAtom>(); } } @Override public synchronized boolean isRecoveryCompleted() { if (m_recoveryDone) { return true; } boolean isRecoveryDone = true; int cnt = 0; for (int i = 0; i < m_perSiteRecoveryDone.length; i++) { isRecoveryDone &= m_perSiteRecoveryDone[i]; if(m_perSiteRecoveryDone[i] == true) cnt++; } if (isRecoveryDone || cnt == this.m_numPartitionsPerSite) { m_recoveryDone = true; } return isRecoveryDone; } public boolean isRecoveryCompletedForSite(int siteId) { int index = siteId ; return m_perSiteRecoveryDone[index]; } // Must synchronize because multiple sites might insert into the log // concurrently @Override public synchronized void log(byte[] logbytes, AtomicBoolean isDurable) { LogDataWithAtom atom = new LogDataWithAtom(logbytes, isDurable); LOG.warn("AriesLogNative : log add atom :: size :"+m_waitingToFlush.size()); //totalLogSize += logbytes.length; //numTransactions++; // Must lock further to avoid insertions during a list swap. synchronized (this) { m_waitingToFlush.add(atom); } } @Override public synchronized void setRecoveryCompleted(int siteId) { int index = siteId ; m_perSiteRecoveryDone[index] = true; } @Override public void run() { LOG.debug("AriesLogNative : wait for recovery completion "); while (!isRecoveryCompleted()) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } LOG.debug("AriesLogNative : recovery completed "); if (!isInitialized) { init(); } LOG.debug("AriesLogNative : initialized log"); while (m_keepLogging) { try { long flushTime = System.currentTimeMillis(); flushData(); flushTime = System.currentTimeMillis() - flushTime; long sleepDuration = fsyncFrequency - flushTime; if (sleepDuration > 0) { Thread.sleep(sleepDuration); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }