package com.sleepycat.je.log; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedList; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.config.EnvironmentParams; import com.sleepycat.je.dbi.DbConfigManager; import com.sleepycat.je.dbi.EnvironmentImpl; import de.ovgu.cide.jakutil.*; /** * LogBufferPool keeps a set of log buffers. */ class LogBufferPool { private static final String DEBUG_NAME=LogBufferPool.class.getName(); private EnvironmentImpl envImpl=null; private int logBufferSize; private LinkedList bufferPool; private LogBuffer currentWriteBuffer; private FileManager fileManager; private boolean runInMemory; LogBufferPool( FileManager fileManager, EnvironmentImpl envImpl) throws DatabaseException { this.fileManager=fileManager; this.envImpl=envImpl; this.hook485(envImpl); DbConfigManager configManager=envImpl.getConfigManager(); runInMemory=configManager.getBoolean(EnvironmentParams.LOG_MEMORY_ONLY); reset(configManager); currentWriteBuffer=(LogBuffer)bufferPool.getFirst(); } /** * Initialize the pool at construction time and when the cache is resized. * This method is called after the memory budget has been calculated. */ void reset( DbConfigManager configManager) throws DatabaseException { if (runInMemory && bufferPool != null) { return; } int numBuffers=configManager.getInt(EnvironmentParams.NUM_LOG_BUFFERS); long logBufferBudget=envImpl.getMemoryBudget().getLogBufferBudget(); int newBufferSize=(int)logBufferBudget / numBuffers; LinkedList newPool=new LinkedList(); if (runInMemory) { numBuffers=1; } for (int i=0; i < numBuffers; i++) { newPool.add(new LogBuffer(newBufferSize,envImpl)); } this.hook486(); bufferPool=newPool; logBufferSize=newBufferSize; } /** * Get a log buffer for writing sizeNeeded bytes. If currentWriteBuffer is * too small or too full, flush currentWriteBuffer and get a new one. * Called within the log write latch. * @return a buffer that can hold sizeNeeded bytes. */ LogBuffer getWriteBuffer( int sizeNeeded, boolean flippedFile) throws IOException, DatabaseException { if ((!currentWriteBuffer.hasRoom(sizeNeeded)) || flippedFile) { writeBufferToFile(sizeNeeded); } if (flippedFile) { if (!runInMemory) { fileManager.syncLogEndAndFinishFile(); } } return currentWriteBuffer; } /** * Write the contents of the currentWriteBuffer to disk. Leave this buffer * in memory to be available to would be readers. Set up a new * currentWriteBuffer. Assumes the log write latch is held. * @param sizeNeeded is the size of the next object we need to write to * the log. May be 0 if this is called on behalf of LogManager.flush(). */ void writeBufferToFile( int sizeNeeded) throws IOException, DatabaseException { int bufferSize=((logBufferSize > sizeNeeded) ? logBufferSize : sizeNeeded); this.hook488(); LogBuffer latchedBuffer=currentWriteBuffer; this.hook487(bufferSize,latchedBuffer); } /** * A loggable object has been freshly marshalled into the write log buffer. * 1. Update buffer so it knows what LSNs it contains. * 2. If this object requires a flush, write this buffer out to the * backing file. * Assumes log write latch is held. */ void writeCompleted( long lsn, boolean flushRequired) throws DatabaseException, IOException { currentWriteBuffer.registerLsn(lsn); if (flushRequired) { writeBufferToFile(0); } } /** * Find a buffer that holds this LSN. * @return the buffer that contains this LSN, latched and ready to * read, or return null. */ LogBuffer getReadBuffer( long lsn) throws DatabaseException { LogBuffer foundBuffer=null; foundBuffer=this.hook489(lsn,foundBuffer); if (foundBuffer == null) { return null; } else { return foundBuffer; } } protected void hook485( EnvironmentImpl envImpl) throws DatabaseException { } protected void hook486() throws DatabaseException { } protected void hook487( int bufferSize, LogBuffer latchedBuffer) throws IOException, DatabaseException { ByteBuffer currentByteBuffer=currentWriteBuffer.getDataBuffer(); int savePosition=currentByteBuffer.position(); int saveLimit=currentByteBuffer.limit(); currentByteBuffer.flip(); if (runInMemory) { this.hook492(latchedBuffer); latchedBuffer=null; this.hook491(); currentWriteBuffer=new LogBuffer(bufferSize,envImpl); bufferPool.add(currentWriteBuffer); this.hook490(); } else { try { fileManager.writeLogBuffer(currentWriteBuffer); currentWriteBuffer.getDataBuffer().rewind(); this.hook494(latchedBuffer); latchedBuffer=null; LogBuffer nextToUse=null; this.hook493(nextToUse); } catch ( DatabaseException DE) { currentByteBuffer.position(savePosition); currentByteBuffer.limit(saveLimit); throw DE; } } } protected void hook488() throws IOException, DatabaseException { } protected LogBuffer hook489( long lsn, LogBuffer foundBuffer) throws DatabaseException { Iterator iter=bufferPool.iterator(); while (iter.hasNext()) { LogBuffer l=(LogBuffer)iter.next(); if (l.containsLsn(lsn)) { foundBuffer=l; break; } } if (foundBuffer == null && currentWriteBuffer.containsLsn(lsn)) { foundBuffer=currentWriteBuffer; } if (foundBuffer == null) { this.hook496(); } return foundBuffer; } protected void hook490() throws IOException, DatabaseException { } protected void hook491() throws IOException, DatabaseException { } protected void hook492( LogBuffer latchedBuffer) throws IOException, DatabaseException { } protected void hook493( LogBuffer nextToUse) throws IOException, DatabaseException { this.hook495(); Iterator iter=bufferPool.iterator(); nextToUse=(LogBuffer)iter.next(); boolean done=bufferPool.remove(nextToUse); assert done; nextToUse.reinit(); bufferPool.add(nextToUse); currentWriteBuffer=nextToUse; } protected void hook494( LogBuffer latchedBuffer) throws IOException, DatabaseException { } protected void hook495() throws IOException, DatabaseException { } protected void hook496() throws DatabaseException { } }