public String toString() { final IRootBlockView tmp = HALogFile.this.getOpeningRootBlock(); final long seq = m_nextSequence; return getClass().getName() + "{" + ((!isOpen()) ? "closed" : "commitCounter=" + tmp.getCommitCounter() + ",nextSequence=" + seq) + "}"; }
private void writeRootBlock(final boolean isRootBlock0, final IRootBlockView rootBlock) throws IOException { if (rootBlock == null) throw new IllegalArgumentException(); final long position = isRootBlock0 ? OFFSET_ROOT_BLOCK0 : OFFSET_ROOT_BLOCK1; FileChannelUtility.writeAll(m_reopener, rootBlock.asReadOnlyBuffer(), position); if (log.isDebugEnabled()) log.debug("wrote root block: " + rootBlock); }
/** * This constructor is called by the log manager to create the file. A writer is created at the * same time, and its presence indicates that the file is open for writing. * * @throws IOException */ public HALogFile(final IRootBlockView rbv, final IHALogManagerCallback callback) throws IOException { m_callback = callback; m_haLogFile = getHALogFileName(m_callback.getHALogDir(), rbv.getCommitCounter()); if (m_haLogFile.exists()) throw new IllegalStateException("File already exists: " + m_haLogFile.getAbsolutePath()); final File parentDir = m_haLogFile.getParentFile(); // Make sure the parent directory(ies) exist. if (!parentDir.exists()) if (!parentDir.mkdirs()) throw new IOException("Could not create directory: " + parentDir); m_raf = new RandomAccessFile(m_haLogFile, "rw"); m_channel = m_raf.getChannel(); m_storeType = rbv.getStoreType(); m_openRootBlock = rbv; m_closeRootBlock = null; // file NOT closed m_magic = MAGIC; m_version = VERSION1; /* * Write the MAGIC and version on the file. */ m_raf.seek(0); m_raf.writeInt(m_magic); m_raf.writeInt(m_version); // Write opening rootblock as both BLOCK0 and BLOCK1 writeRootBlock(true, rbv); // as BLOCK0 writeRootBlock(false, rbv); // as BLOCK1 m_writePosition = START_DATA; m_writer = new HALogWriter(); if (log.isInfoEnabled()) log.info("Opening HALogFile: " + m_haLogFile.getAbsolutePath()); }
/** * Called by the HALogWriter to close the log file with the committing rootblock. * * @param rbv * @throws IOException */ private void close(final IRootBlockView rbv) throws IOException { m_writeLock.lock(); try { if (m_closeRootBlock != null) throw new IllegalStateException("LogFile is already closed"); writeRootBlock(rbv.isRootBlock0(), rbv); m_closeRootBlock = rbv; m_callback.release(this); m_fileChange.signalAll(); } finally { m_writeLock.unlock(); } }
public long getCommitCounter() { return m_openRootBlock.getCommitCounter(); }
/** @return true if the file is complete and its size indicates no content */ public boolean isEmpty() { return (m_closeRootBlock != null) && (m_openRootBlock.getCommitCounter() == m_closeRootBlock.getCommitCounter()); }
/** * Private method called by the HALogWriter to write a new message * * @param msg * @param data * @throws IOException */ private void write(final IHAWriteMessage msg, final ByteBuffer data) throws IOException { m_writeLock.lock(); try { /* * Check if this really is a valid message for this file. If it is * not, then close the file and return immediately */ if (m_openRootBlock.getCommitCounter() != msg.getCommitCounter()) throw new IllegalStateException( "commitCounter=" + m_openRootBlock.getCommitCounter() + ", but msg=" + msg); if (m_openRootBlock.getLastCommitTime() != msg.getLastCommitTime()) throw new IllegalStateException( "lastCommitTime=" + m_openRootBlock.getLastCommitTime() + ", but msg=" + msg); if (m_sequence != msg.getSequence()) throw new IllegalStateException("nextSequence=" + m_sequence + ", but msg=" + msg); if (log.isInfoEnabled()) log.info("msg=" + msg + ", position=" + m_writePosition); if (m_writePosition < headerSize0) throw new AssertionError("position=" + m_writePosition + ", but headerSize=" + headerSize0); /* * Write the HAWriteMessage onto the channel. */ { // serialized message object (pos=0; limit=nbytes) final ByteBuffer tmp = bufferObject(msg); final int nbytes = tmp.limit(); FileChannelUtility.writeAll(m_reopener, tmp, m_writePosition); m_writePosition += nbytes; } switch (m_openRootBlock.getStoreType()) { case RW: { /* * Write the WriteCache block on the channel. */ final int nbytes = msg.getSize(); assert data.position() == 0; assert data.limit() == nbytes; // Note: duplicate() to avoid side effects on ByteBuffer!!! FileChannelUtility.writeAll(m_reopener, data.duplicate(), m_writePosition); m_writePosition += nbytes; break; } case WORM: { /* * We will use the HA failover read API to recover the block * from a node in the quorum when we need to replay the HA log. */ break; } default: throw new AssertionError(); } m_sequence++; m_fileChange.signalAll(); } finally { m_writeLock.unlock(); } }
/** * This constructor creates a read only view of the log file and can be used independently of the * HALogManager. * * <p>The opening and closing root blocks are examined to confirm the file has fully committed * writes and can be opened for read only access. * * @param readonlyLog * @throws FileNotFoundException */ public HALogFile(final File file) { if (file == null || !file.exists()) throw new IllegalStateException(); m_callback = null; m_writer = null; m_haLogFile = file; try { m_raf = new RandomAccessFile(m_haLogFile, "r"); } catch (FileNotFoundException e) { // this should have been caught above and thrown // IllegalStateException throw new RuntimeException(e); } m_channel = m_raf.getChannel(); try { /** * Must determine whether the file has consistent open and committed rootBlocks, using the * commitCounter to determine which rootBlock is which. * * <p>Note: Both root block should exist (they are both written on startup). If they are * identical, then the log is empty (the closing root block has not been written and the data * in the log is useless). * * <p>We figure out which root block is the opening root block based on standard logic. */ /* * Read the MAGIC and VERSION. */ m_raf.seek(0L); try { /* * Note: this next line will throw IOException if there is a * file lock contention. */ m_magic = m_raf.readInt(); } catch (IOException ex) { throw new RuntimeException("Can not read magic. Is file locked by another process?", ex); } if (m_magic != MAGIC) throw new RuntimeException("Bad journal magic: expected=" + MAGIC + ", actual=" + m_magic); m_version = m_raf.readInt(); if (m_version != VERSION1) throw new RuntimeException( "Bad journal version: expected=" + VERSION1 + ", actual=" + m_version); final RootBlockUtility tmp = new RootBlockUtility( m_reopener, file, true /* validateChecksum */, false /* alternateRootBlock */, false /* ignoreBadRootBlock */); m_closeRootBlock = tmp.chooseRootBlock(); m_openRootBlock = tmp.rootBlock0 == m_closeRootBlock ? tmp.rootBlock1 : tmp.rootBlock0; final long cc0 = m_openRootBlock.getCommitCounter(); final long cc1 = m_closeRootBlock.getCommitCounter(); if ((cc0 + 1) != cc1 && (cc0 != cc1)) { /* * Counters are inconsistent with either an empty log file or a * single transaction scope. */ throw new IllegalStateException("Incompatible rootblocks: cc0=" + cc0 + ", cc1=" + cc1); } m_channel.position(START_DATA); m_storeType = m_openRootBlock.getStoreType(); } catch (Throwable t) { try { close(); } catch (IOException e) { log.warn(e); } throw new RuntimeException(t); } }