001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: LogBufferPool.java,v 1.72.2.3 2008/01/07 15:14:13 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.io.IOException;
012: import java.nio.ByteBuffer;
013: import java.util.Iterator;
014: import java.util.LinkedList;
015:
016: import com.sleepycat.je.DatabaseException;
017: import com.sleepycat.je.EnvironmentStats;
018: import com.sleepycat.je.StatsConfig;
019: import com.sleepycat.je.config.EnvironmentParams;
020: import com.sleepycat.je.dbi.DbConfigManager;
021: import com.sleepycat.je.dbi.EnvironmentImpl;
022: import com.sleepycat.je.latch.Latch;
023: import com.sleepycat.je.latch.LatchSupport;
024:
025: /**
026: * LogBufferPool keeps a set of log buffers.
027: */
028: class LogBufferPool {
029: private static final String DEBUG_NAME = LogBufferPool.class
030: .getName();
031:
032: private EnvironmentImpl envImpl = null;
033: private int logBufferSize; // size of each log buffer
034: private LinkedList bufferPool; // List of log buffers
035:
036: /* Buffer that holds the current log end. All writes go to this buffer. */
037: private LogBuffer currentWriteBuffer;
038:
039: private FileManager fileManager;
040:
041: /* Stats */
042: private long nNotResident = 0; // had to be instantiated from an lsn
043: private long nCacheMiss = 0; // had to retrieve from disk
044: private boolean runInMemory;
045:
046: /*
047: * bufferPoolLatch is synchronizes access and changes to the buffer pool.
048: * Related latches are the log write latch in LogManager and the read
049: * latches in each log buffer. The log write latch is always taken before
050: * the bufferPoolLatch. The bufferPoolLatch is always taken before any
051: * logBuffer read latch. When faulting in an object from the log, the order
052: * of latching is:
053: * bufferPoolLatch.acquire()
054: * LogBuffer read latch acquire();
055: * bufferPoolLatch.release();
056: * LogBuffer read latch release()
057: * bufferPoolLatch is also used to protect assignment to the
058: * currentWriteBuffer field.
059: */
060: private Latch bufferPoolLatch;
061:
062: LogBufferPool(FileManager fileManager, EnvironmentImpl envImpl)
063: throws DatabaseException {
064:
065: this .fileManager = fileManager;
066: this .envImpl = envImpl;
067: bufferPoolLatch = LatchSupport.makeLatch(DEBUG_NAME
068: + "_FullLatch", envImpl);
069:
070: /* Configure the pool. */
071: DbConfigManager configManager = envImpl.getConfigManager();
072: runInMemory = envImpl.isMemOnly();
073: reset(configManager);
074:
075: /* Current buffer is the active buffer that writes go into. */
076: currentWriteBuffer = (LogBuffer) bufferPool.getFirst();
077: }
078:
079: final int getLogBufferSize() {
080: return logBufferSize;
081: }
082:
083: /**
084: * Initialize the pool at construction time and when the cache is resized.
085: * This method is called after the memory budget has been calculated.
086: */
087: void reset(DbConfigManager configManager) throws DatabaseException {
088:
089: /*
090: * When running in memory, we can't clear the existing pool and
091: * changing the buffer size is not very useful, so just return.
092: */
093: if (runInMemory && bufferPool != null) {
094: return;
095: }
096:
097: /*
098: * Based on the log budget, figure the number and size of
099: * log buffers to use.
100: */
101: int numBuffers = configManager
102: .getInt(EnvironmentParams.NUM_LOG_BUFFERS);
103: long logBufferBudget = envImpl.getMemoryBudget()
104: .getLogBufferBudget();
105:
106: /* Buffers must be int sized. */
107: int newBufferSize = (int) logBufferBudget / numBuffers;
108:
109: /* list of buffers that are available for log writing */
110: LinkedList newPool = new LinkedList();
111:
112: /*
113: * If we're running in memory only, don't pre-allocate all the buffers.
114: * This case only occurs when called from the constructor.
115: */
116: if (runInMemory) {
117: numBuffers = 1;
118: }
119:
120: for (int i = 0; i < numBuffers; i++) {
121: newPool.add(new LogBuffer(newBufferSize, envImpl));
122: }
123:
124: /*
125: * The following applies when this method is called to reset the pool
126: * when an existing pool is in use:
127: * - The old pool will no longer be referenced.
128: * - Buffers being read in the old pool will be no longer referenced
129: * after the read operation is complete.
130: * - The currentWriteBuffer field is not changed here; it will be no
131: * longer referenced after it is written to the file and a new
132: * currentWriteBuffer is assigned.
133: * - The logBufferSize can be changed now because it is only used for
134: * allocating new buffers; it is not used as the size of the
135: * currentWriteBuffer.
136: */
137: bufferPoolLatch.acquire();
138: bufferPool = newPool;
139: logBufferSize = newBufferSize;
140: bufferPoolLatch.release();
141: }
142:
143: /**
144: * Get a log buffer for writing sizeNeeded bytes. If currentWriteBuffer is
145: * too small or too full, flush currentWriteBuffer and get a new one.
146: * Called within the log write latch.
147: *
148: * @return a buffer that can hold sizeNeeded bytes.
149: */
150: LogBuffer getWriteBuffer(int sizeNeeded, boolean flippedFile)
151: throws IOException, DatabaseException {
152:
153: /*
154: * We need a new log buffer either because this log buffer is full, or
155: * the LSN has marched along to the next file. Each log buffer only
156: * holds entries that belong to a single file. If we've flipped over
157: * into the next file, we'll need to get a new log buffer even if the
158: * current one has room.
159: */
160: if ((!currentWriteBuffer.hasRoom(sizeNeeded)) || flippedFile) {
161:
162: /*
163: * Write the currentWriteBuffer to the file and reset
164: * currentWriteBuffer.
165: */
166: writeBufferToFile(sizeNeeded);
167: }
168:
169: if (flippedFile) {
170: /* Now that the old buffer has been written to disk, fsync. */
171: if (!runInMemory) {
172: fileManager.syncLogEndAndFinishFile();
173: }
174: }
175:
176: return currentWriteBuffer;
177: }
178:
179: /**
180: * Write the contents of the currentWriteBuffer to disk. Leave this buffer
181: * in memory to be available to would be readers. Set up a new
182: * currentWriteBuffer. Assumes the log write latch is held.
183: *
184: * @param sizeNeeded is the size of the next object we need to write to
185: * the log. May be 0 if this is called on behalf of LogManager.flush().
186: */
187: void writeBufferToFile(int sizeNeeded) throws IOException,
188: DatabaseException {
189:
190: int bufferSize = ((logBufferSize > sizeNeeded) ? logBufferSize
191: : sizeNeeded);
192:
193: /* We're done with the buffer, flip to make it readable. */
194: currentWriteBuffer.latchForWrite();
195: LogBuffer latchedBuffer = currentWriteBuffer;
196: try {
197: ByteBuffer currentByteBuffer = currentWriteBuffer
198: .getDataBuffer();
199: int savePosition = currentByteBuffer.position();
200: int saveLimit = currentByteBuffer.limit();
201: currentByteBuffer.flip();
202:
203: /* Dispose of it and get a new buffer for writing. */
204: if (runInMemory) {
205: /* We're done with the current buffer. */
206: latchedBuffer.release();
207: latchedBuffer = null;
208: /* We're supposed to run in-memory, allocate another buffer. */
209: bufferPoolLatch.acquire();
210: currentWriteBuffer = new LogBuffer(bufferSize, envImpl);
211: bufferPool.add(currentWriteBuffer);
212: bufferPoolLatch.release();
213: } else {
214:
215: /*
216: * If we're configured for writing (not memory-only situation),
217: * write this buffer to disk and find a new buffer to use.
218: */
219: try {
220: fileManager.writeLogBuffer(currentWriteBuffer);
221:
222: /* Rewind so readers can see this. */
223: currentWriteBuffer.getDataBuffer().rewind();
224:
225: /* We're done with the current buffer. */
226: latchedBuffer.release();
227: latchedBuffer = null;
228:
229: /*
230: * Now look in the linked list for a buffer of the right
231: * size.
232: */
233: LogBuffer nextToUse = null;
234: try {
235: bufferPoolLatch.acquire();
236: Iterator iter = bufferPool.iterator();
237: nextToUse = (LogBuffer) iter.next();
238:
239: boolean done = bufferPool.remove(nextToUse);
240: assert done;
241: nextToUse.reinit();
242:
243: /* Put the nextToUse buffer at the end of the queue. */
244: bufferPool.add(nextToUse);
245:
246: /* Assign currentWriteBuffer with the latch held. */
247: currentWriteBuffer = nextToUse;
248: } finally {
249: bufferPoolLatch.releaseIfOwner();
250: }
251: } catch (DatabaseException DE) {
252: currentByteBuffer.position(savePosition);
253: currentByteBuffer.limit(saveLimit);
254: throw DE;
255: }
256: }
257: } finally {
258: if (latchedBuffer != null) {
259: latchedBuffer.release();
260: }
261: }
262: }
263:
264: /**
265: * A loggable object has been freshly marshalled into the write log buffer.
266: * 1. Update buffer so it knows what LSNs it contains.
267: * 2. If this object requires a flush, write this buffer out to the
268: * backing file.
269: * Assumes log write latch is held.
270: */
271: void writeCompleted(long lsn, boolean flushRequired)
272: throws DatabaseException, IOException {
273:
274: currentWriteBuffer.registerLsn(lsn);
275: if (flushRequired) {
276: writeBufferToFile(0);
277: }
278: }
279:
280: /**
281: * Find a buffer that holds this LSN.
282: * @return the buffer that contains this LSN, latched and ready to
283: * read, or return null.
284: */
285: LogBuffer getReadBuffer(long lsn) throws DatabaseException {
286:
287: LogBuffer foundBuffer = null;
288:
289: bufferPoolLatch.acquire();
290: try {
291: nNotResident++;
292: Iterator iter = bufferPool.iterator();
293: while (iter.hasNext()) {
294: LogBuffer l = (LogBuffer) iter.next();
295: if (l.containsLsn(lsn)) {
296: foundBuffer = l;
297: break;
298: }
299: }
300:
301: /*
302: * Check the currentWriteBuffer separately, since if the pool was
303: * recently reset it will not be in the pool.
304: */
305: if (foundBuffer == null
306: && currentWriteBuffer.containsLsn(lsn)) {
307: foundBuffer = currentWriteBuffer;
308: }
309:
310: if (foundBuffer == null) {
311: nCacheMiss++;
312: }
313:
314: } finally {
315: bufferPoolLatch.releaseIfOwner();
316: }
317:
318: if (foundBuffer == null) {
319: return null;
320: } else {
321: return foundBuffer;
322: }
323: }
324:
325: void loadStats(StatsConfig config, EnvironmentStats stats)
326: throws DatabaseException {
327:
328: stats.setNCacheMiss(nCacheMiss);
329: stats.setNNotResident(nNotResident);
330: if (config.getClear()) {
331: nCacheMiss = 0;
332: nNotResident = 0;
333: }
334:
335: /* Also return buffer pool memory usage */
336: bufferPoolLatch.acquire();
337: long bufferBytes = 0;
338: int nLogBuffers = 0;
339: try {
340: Iterator iter = bufferPool.iterator();
341: while (iter.hasNext()) {
342: LogBuffer l = (LogBuffer) iter.next();
343: nLogBuffers++;
344: bufferBytes += l.getCapacity();
345: }
346: } finally {
347: bufferPoolLatch.release();
348: }
349: stats.setNLogBuffers(nLogBuffers);
350: stats.setBufferBytes(bufferBytes);
351: }
352: }
|