001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.log.LogAccessFile
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.raw.log;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027: import org.apache.derby.iapi.error.StandardException;
028:
029: import org.apache.derby.io.StorageRandomAccessFile;
030:
031: import java.io.IOException;
032: import java.io.OutputStream;
033: import java.io.SyncFailedException;
034: import java.io.InterruptedIOException;
035: import java.util.LinkedList;
036:
037: import org.apache.derby.iapi.services.io.FormatIdOutputStream;
038: import org.apache.derby.iapi.services.io.ArrayOutputStream;
039: import org.apache.derby.iapi.store.raw.RawStoreFactory;
040:
041: /**
042: Wraps a RandomAccessFile file to provide buffering
043: on log writes. Only supports the write calls
044: required for the log!
045:
046: MT - unsafe. Caller of this class must provide synchronization. The one
047: exception is with the log file access, LogAccessFile will touch the log
048: only inside synchronized block protected by the semaphore, which is
049: defined by the creator of this object.
050:
051: Write to the log buffers are allowed when there are free buffers even
052: when dirty buffers are being written(flushed) to the disk by a different
053: thread. Only one flush writes to log file at a time, other wait for it to finish.
054:
055: Except for flushLogAccessFile , SyncAccessLogFile other function callers
056: must provide syncronization that will allow only one of them to write to
057: the buffers.
058:
059: Log Buffers are used in circular fashion, each buffer moves through following stages:
060: freeBuffers --> dirtyBuffers --> freeBuffers. Movement of buffers from one
061: stage to another stage is synchronized using the object(this) of this class.
062:
063: A Checksum log record that has the checksum value for the data that is
064: being written to the disk is generated and written before the actual data.
065: Except for the large log records that does not fit into a single buffer,
066: checksum is calcualted for a group of log records that are in the buffer
067: when buffers is switched. Checksum log record is written into the reserved
068: space in the beginning buffer.
069:
070: In case of a large log record that does not fit into a bufffer, it needs to
071: be written directly to the disk instead of going through the log buffers.
072: In this case the log record write gets broken into three parts:
073: 1) Write checksum log record and LOG RECORD HEADER (length + instant)
074: 2) Write the log record.
075: 3) Write the trailing length of the log record.
076:
077: Checksum log records helps in identifying the incomplete log disk writes during
078: recovery. This is done by recalculating the checksum value for the data on
079: the disk and comparing it to the the value stored in the checksum log
080: record.
081:
082: */
083: public class LogAccessFile {
084:
085: /**
086: * The fixed size of a log record is 16 bytes:
087: * int length : 4 bytes
088: * long instant : 8 bytes
089: * int trailing length : 4 bytes
090: **/
091: private static final int LOG_RECORD_FIXED_OVERHEAD_SIZE = 16;
092: private static final int LOG_RECORD_HEADER_SIZE = 12; //(length + instant)
093: private static final int LOG_RECORD_TRAILER_SIZE = 4; //trailing length
094: private static final int LOG_NUMBER_LOG_BUFFERS = 3;
095:
096: private LinkedList freeBuffers; //list of free buffers
097: private LinkedList dirtyBuffers; //list of dirty buffers to flush
098: private LogAccessFileBuffer currentBuffer; //current active buffer
099: private boolean flushInProgress = false;
100:
101: private final StorageRandomAccessFile log;
102:
103: // log can be touched only inside synchronized block protected by
104: // logFileSemaphore.
105: private final Object logFileSemaphore;
106:
107: static int mon_numWritesToLog;
108: static int mon_numBytesToLog;
109:
110: //streams used to generated check sume log record ; see if there is any simpler way
111: private ArrayOutputStream logOutputBuffer;
112: private FormatIdOutputStream logicalOut;
113: private boolean directWrite = false; //true when log is written directly to file.
114: private long checksumInstant = -1;
115: private int checksumLength;
116: private int checksumLogRecordSize; //checksumLength + LOG_RECORD_FIXED_OVERHEAD_SIZE
117: private boolean writeChecksum;
118: private ChecksumOperation checksumLogOperation;
119: private LogRecord checksumLogRecord;
120: private LogToFile logFactory;
121: private boolean databaseEncrypted = false;
122:
123: public LogAccessFile(LogToFile logFactory,
124: StorageRandomAccessFile log, int bufferSize) {
125: if (SanityManager.DEBUG) {
126: if (SanityManager.DEBUG_ON("LogBufferOff"))
127: bufferSize = 10; // make it very tiny
128: }
129:
130: this .log = log;
131: logFileSemaphore = log;
132: this .logFactory = logFactory;
133:
134: if (SanityManager.DEBUG)
135: SanityManager.ASSERT(LOG_NUMBER_LOG_BUFFERS >= 1);
136:
137: //initialize buffers lists
138: freeBuffers = new LinkedList();
139: dirtyBuffers = new LinkedList();
140:
141: //add all buffers to free list
142: for (int i = 0; i < LOG_NUMBER_LOG_BUFFERS; i++) {
143: LogAccessFileBuffer b = new LogAccessFileBuffer(bufferSize);
144: freeBuffers.addLast(b);
145: }
146:
147: currentBuffer = (LogAccessFileBuffer) freeBuffers.removeFirst();
148:
149: // Support for Transaction Log Checksum in Derby was added in 10.1
150: // Check to see if the Store have been upgraded to 10.1 or later before
151: // writing the checksum log records. Otherwise recovery will fail
152: // incase user tries to revert back to versions before 10.1 in
153: // soft upgrade mode.
154: writeChecksum = logFactory.checkVersion(
155: RawStoreFactory.DERBY_STORE_MAJOR_VERSION_10,
156: RawStoreFactory.DERBY_STORE_MINOR_VERSION_1);
157: if (writeChecksum) {
158: /**
159: * setup structures that are required to write the checksum log records
160: * for a group of log records are being written to the disk.
161: */
162: checksumLogOperation = new ChecksumOperation();
163: checksumLogOperation.init();
164: checksumLogRecord = new LogRecord();
165:
166: // Note: Checksum log records are not related any particular transaction,
167: // they are written to store a checksum information identify
168: // incomplete log record writes. No transacton id is set for this
169: // log record. That is why a null argument is passed below
170: // setValue(..) call.
171: checksumLogRecord.setValue(null, checksumLogOperation);
172:
173: checksumLength = checksumLogRecord.getStoredSize(
174: checksumLogOperation.group(), null)
175: + checksumLogOperation.getStoredSize();
176:
177: // calculate checksum log operation length when the database is encrypted
178: if (logFactory.databaseEncrypted()) {
179: checksumLength = logFactory
180: .getEncryptedDataLength(checksumLength);
181: databaseEncrypted = true;
182: }
183: checksumLogRecordSize = checksumLength
184: + LOG_RECORD_FIXED_OVERHEAD_SIZE;
185:
186: //streams required to convert a log record to raw byte array.
187: logOutputBuffer = new ArrayOutputStream();
188: logicalOut = new FormatIdOutputStream(logOutputBuffer);
189:
190: /** initialize the buffer with space reserved for checksum log record in
191: * the beginning of the log buffer; checksum record is written into
192: * this space when buffer is switched or while doing direct write to the log file.
193: */
194: } else {
195: //checksumming of transaction log feature is not in use.
196: checksumLogRecordSize = 0;
197: }
198:
199: currentBuffer.init(checksumLogRecordSize);
200: }
201:
202: private byte[] db = new byte[LOG_RECORD_TRAILER_SIZE];
203:
204: /**
205: * Write a single log record to the stream.
206: * <p>
207: * For performance pass all parameters rather into a specialized routine
208: * rather than maintaining the writeInt, writeLong, and write interfaces
209: * that this class provides as a standard OutputStream. It will make it
210: * harder to use other OutputStream implementations, but makes for less
211: * function calls and allows optimizations knowing when to switch buffers.
212: * <p>
213: * This routine handles all log records which are smaller than one log
214: * buffer. If a log record is bigger than a log buffer it calls
215: * writeUnbufferedLogRecord().
216: * <p>
217: * The log record written will always look the same as if the following
218: * code had been executed:
219: * writeInt(length)
220: * writeLong(instant)
221: * write(data, data_offset, (length - optional_data_length) )
222: *
223: * if (optional_data_length != 0)
224: * write(optional_data, optional_data_offset, optional_data_length)
225: *
226: * writeInt(length)
227: *
228: * @param length (data + optional_data) length bytes to write
229: * @param instant the log address of this log record.
230: * @param data "from" array to copy "data" portion of rec
231: * @param data_offset offset in "data" to start copying from.
232: * @param optional_data "from" array to copy "optional data" from
233: * @param optional_data_offset offset in "optional_data" to start copy from
234: * @param optional_data_length length of optional data to copy.
235: *
236: * @exception StandardException Standard exception policy.
237: **/
238: public void writeLogRecord(int length, long instant, byte[] data,
239: int data_offset, byte[] optional_data,
240: int optional_data_offset, int optional_data_length)
241: throws StandardException, IOException {
242: int total_log_record_length = length
243: + LOG_RECORD_FIXED_OVERHEAD_SIZE;
244:
245: if (total_log_record_length <= currentBuffer.bytes_free) {
246: byte[] b = currentBuffer.buffer;
247: int p = currentBuffer.position;
248:
249: // writeInt(length)
250: p = writeInt(length, b, p);
251:
252: // writeLong(instant)
253: p = writeLong(instant, b, p);
254:
255: // write(data, data_offset, length - optional_data_length)
256: int transfer_length = (length - optional_data_length);
257: System.arraycopy(data, data_offset, b, p, transfer_length);
258:
259: p += transfer_length;
260:
261: if (optional_data_length != 0) {
262: // write(
263: // optional_data, optional_data_offset, optional_data_length);
264:
265: System.arraycopy(optional_data, optional_data_offset,
266: b, p, optional_data_length);
267:
268: p += optional_data_length;
269: }
270:
271: // writeInt(length)
272: p = writeInt(length, b, p);
273:
274: currentBuffer.position = p;
275: currentBuffer.bytes_free -= total_log_record_length;
276: } else {
277:
278: /** Because current log record will never fit in a single buffer
279: * a direct write to the log file is required instead of
280: * writing the log record through the log bufffers.
281: */
282: directWrite = true;
283:
284: byte[] b = currentBuffer.buffer;
285: int p = currentBuffer.position;
286:
287: // writeInt(length)
288: p = writeInt(length, b, p);
289:
290: // writeLong(instant)
291: p = writeLong(instant, b, p);
292:
293: currentBuffer.position = p;
294: currentBuffer.bytes_free -= LOG_RECORD_HEADER_SIZE;
295:
296: /** using a seperate small buffer to write the traling length
297: * instead of the log buffer because data portion will be
298: * written directly to log file after the log buffer is
299: * flushed and the trailing length should be written after that.
300: */
301:
302: // writeInt(length)
303: writeInt(length, db, 0);
304:
305: if (writeChecksum) {
306: checksumLogOperation.reset();
307: checksumLogOperation.update(b, checksumLogRecordSize, p
308: - checksumLogRecordSize);
309: checksumLogOperation.update(data, data_offset, length
310: - optional_data_length);
311: if (optional_data_length != 0) {
312: checksumLogOperation.update(optional_data,
313: optional_data_offset, optional_data_length);
314: }
315:
316: // update the checksum to include the trailing length.
317: checksumLogOperation.update(db, 0,
318: LOG_RECORD_TRAILER_SIZE);
319:
320: // write checksum log record to the log buffer
321: writeChecksumLogRecord();
322: }
323:
324: // now do the writes directly to the log file.
325:
326: // flush all buffers before wrting directly to the log file.
327: flushLogAccessFile();
328:
329: // Note:No Special Synchronization required here ,
330: // There will be nothing to write by flushDirtyBuffers that can run
331: // in parallel to the threads that is executing this code. Above
332: // flush call should have written all the buffers and NO new log will
333: // get added until the following direct log to file call finishes.
334:
335: // write the rest of the log directltly to the log file.
336: writeToLog(data, data_offset, length - optional_data_length);
337: if (optional_data_length != 0) {
338: writeToLog(optional_data, optional_data_offset,
339: optional_data_length);
340: }
341:
342: // write the trailing length
343: writeToLog(db, 0, 4);
344: directWrite = false;
345: }
346: }
347:
348: private final int writeInt(int i, byte b[], int p) {
349:
350: b[p++] = (byte) ((i >>> 24) & 0xff);
351: b[p++] = (byte) ((i >>> 16) & 0xff);
352: b[p++] = (byte) ((i >>> 8) & 0xff);
353: b[p++] = (byte) (i & 0xff);
354: return p;
355: }
356:
357: private final int writeLong(long l, byte b[], int p) {
358: b[p++] = (byte) (((int) (l >>> 56)) & 0xff);
359: b[p++] = (byte) (((int) (l >>> 48)) & 0xff);
360: b[p++] = (byte) (((int) (l >>> 40)) & 0xff);
361: b[p++] = (byte) (((int) (l >>> 32)) & 0xff);
362: b[p++] = (byte) (((int) (l >>> 24)) & 0xff);
363: b[p++] = (byte) (((int) (l >>> 16)) & 0xff);
364: b[p++] = (byte) (((int) (l >>> 8)) & 0xff);
365: b[p++] = (byte) (((int) l) & 0xff);
366: return p;
367: }
368:
369: public void writeInt(int i) {
370:
371: if (SanityManager.DEBUG) {
372: SanityManager.ASSERT(currentBuffer.bytes_free >= 4);
373: }
374:
375: currentBuffer.position = writeInt(i, currentBuffer.buffer,
376: currentBuffer.position);
377: currentBuffer.bytes_free -= 4;
378: }
379:
380: public void writeLong(long l) {
381:
382: if (SanityManager.DEBUG) {
383: SanityManager.ASSERT(currentBuffer.bytes_free >= 8);
384: }
385:
386: currentBuffer.position = writeLong(l, currentBuffer.buffer,
387: currentBuffer.position);
388: currentBuffer.bytes_free -= 8;
389: }
390:
391: public void write(int b) {
392: if (SanityManager.DEBUG) {
393: SanityManager.ASSERT(currentBuffer.bytes_free > 0);
394: }
395:
396: currentBuffer.buffer[currentBuffer.position++] = (byte) b;
397: currentBuffer.bytes_free--;
398: }
399:
400: public void write(byte b[], int off, int len) {
401: if (SanityManager.DEBUG) {
402: SanityManager.ASSERT(len <= currentBuffer.bytes_free);
403: }
404:
405: System.arraycopy(b, off, currentBuffer.buffer,
406: currentBuffer.position, len);
407: currentBuffer.bytes_free -= len;
408: currentBuffer.position += len;
409: }
410:
411: /**
412: * Write data from all dirty buffers into the log file.
413: * <p>
414: * A call for clients of LogAccessFile to insure that all privately buffered
415: * data has been writen to the file - so that reads on the file using one
416: * of the various scan classes will see
417: * all the data which has been writen to this point.
418: * <p>
419: * Note that this routine only "writes" the data to the file, this does not
420: * mean that the data has been synced to disk unless file was opened in
421: * WRITE SYNC mode(rws/rwd). The only way to insure that is by calling
422: * is to call syncLogAccessFile() after this call in Non-WRITE sync mode(rw)
423: *
424: * <p>
425: * MT-Safe : parallel thereads can call this function, only one threads does
426: * the flush and the other threads waits for the one that is doing the flush to finish.
427: * Currently there are two possible threads that can call this function in parallel
428: * 1) A Thread that is doing the commit
429: * 2) A Thread that is writing to the log and log buffers are full or
430: * a log records does not fit in a buffer. (Log Buffers
431: * full(switchLogBuffer() or a log record size that is greater than
432: * logbuffer size has to be writtern through writeToLog call directlty)
433: * Note: writeToLog() is not synchronized on the semaphore
434: * that is used to do buffer management to allow writes
435: * to the free buffers when flush is in progress.
436: **/
437: protected void flushDirtyBuffers() throws IOException {
438: LogAccessFileBuffer buf = null;
439: int noOfBuffers;
440: int nFlushed = 0;
441: try {
442: synchronized (this ) {
443: /**if some one else flushing wait, otherwise it is possible
444: * different threads will get different buffers and order can
445: * not be determined.
446: *
447: **/
448: while (flushInProgress) {
449: try {
450: wait();
451: } catch (InterruptedException ie) {
452: //do nothing, let the flush request to complete.
453: //because it possible that other thread which is
454: //currently might have completed this request also ,
455: //if exited on interrupt and throw exception, can not
456: //be sure whether this transaction is COMMITTED ot not.
457: }
458: }
459:
460: noOfBuffers = dirtyBuffers.size();
461: if (noOfBuffers > 0)
462: buf = (LogAccessFileBuffer) dirtyBuffers
463: .removeFirst();
464:
465: flushInProgress = true;
466: }
467:
468: while (nFlushed < noOfBuffers) {
469: if (buf.position != 0)
470: writeToLog(buf.buffer, 0, buf.position);
471:
472: nFlushed++;
473: synchronized (this ) {
474: //add the buffer that was written previosly to the free list
475: freeBuffers.addLast(buf);
476: if (nFlushed < noOfBuffers)
477: buf = (LogAccessFileBuffer) dirtyBuffers
478: .removeFirst();
479: else {
480: //see if we can flush more, that came when we are at it.
481: //don't flush more than the total number of buffers,
482: //that might lead to starvation of the current thread.
483: int size = dirtyBuffers.size();
484: if (size > 0
485: && nFlushed <= LOG_NUMBER_LOG_BUFFERS) {
486: noOfBuffers += size;
487: buf = (LogAccessFileBuffer) dirtyBuffers
488: .removeFirst();
489: }
490: }
491: }
492: }
493:
494: } finally {
495: synchronized (this ) {
496: flushInProgress = false;
497: notifyAll();
498: }
499: }
500: }
501:
502: //flush all the the dirty buffers to disk
503: public void flushLogAccessFile() throws IOException,
504: StandardException {
505: switchLogBuffer();
506: flushDirtyBuffers();
507: }
508:
509: /**
510: * Appends the current Buffer to the dirty Buffer list and assigns a free
511: * buffer to be the currrent active buffer . Flushing of the buffer
512: * to disk is delayed if there is a free buffer available.
513: * dirty buffers will be flushed to the disk
514: * when flushDirtyBuffers() is invoked by a commit call
515: * or when no more free buffers are available.
516: */
517: public void switchLogBuffer() throws IOException, StandardException {
518:
519: synchronized (this ) {
520: // ignore empty buffer switch requests
521: if (currentBuffer.position == checksumLogRecordSize)
522: return;
523:
524: // calculate the checksum for the current log buffer
525: // and write the record to the space reserverd in
526: // the beginning of the buffer.
527: if (writeChecksum && !directWrite) {
528: checksumLogOperation.reset();
529: checksumLogOperation.update(currentBuffer.buffer,
530: checksumLogRecordSize, currentBuffer.position
531: - checksumLogRecordSize);
532: writeChecksumLogRecord();
533: }
534:
535: //add the current buffer to the flush buffer list
536: dirtyBuffers.addLast(currentBuffer);
537:
538: //if there is No free buffer, flush the buffers to get a free one
539: if (freeBuffers.size() == 0) {
540: flushDirtyBuffers();
541: //after the flush call there should be a free buffer
542: //because this is only methods removes items from
543: //free buffers and removal is in synchronized block.
544: }
545:
546: // there should be free buffer available at this point.
547: if (SanityManager.DEBUG)
548: SanityManager.ASSERT(freeBuffers.size() > 0);
549:
550: //switch over to the next log buffer, let someone else write it.
551: currentBuffer = (LogAccessFileBuffer) freeBuffers
552: .removeFirst();
553: currentBuffer.init(checksumLogRecordSize);
554:
555: if (SanityManager.DEBUG) {
556: SanityManager
557: .ASSERT(currentBuffer.position == checksumLogRecordSize);
558: SanityManager
559: .ASSERT(currentBuffer.bytes_free == currentBuffer.length);
560: SanityManager.ASSERT(currentBuffer.bytes_free > 0);
561: }
562: }
563: }
564:
565: /**
566: * Guarantee all writes up to the last call to flushLogAccessFile on disk.
567: * <p>
568: * A call for clients of LogAccessFile to insure that all data written
569: * up to the last call to flushLogAccessFile() are written to disk.
570: * This call will not return until those writes have hit disk.
571: * <p>
572: * Note that this routine may block waiting for I/O to complete so
573: * callers should limit the number of resource held locked while this
574: * operation is called. It is expected that the caller
575: * Note that this routine only "writes" the data to the file, this does not
576: * mean that the data has been synced to disk. The only way to insure that
577: * is to first call switchLogBuffer() and then follow by a call of sync().
578: *
579: **/
580: public void syncLogAccessFile() throws IOException,
581: StandardException {
582: for (int i = 0;;) {
583: // 3311: JVM sync call sometimes fails under high load against NFS
584: // mounted disk. We re-try to do this 20 times.
585: try {
586: synchronized (this ) {
587: log.sync(false);
588: }
589:
590: // the sync succeed, so return
591: break;
592: } catch (SyncFailedException sfe) {
593: i++;
594: try {
595: // wait for .2 of a second, hopefully I/O is done by now
596: // we wait a max of 4 seconds before we give up
597: Thread.sleep(200);
598: } catch (InterruptedException ie) { //does not matter weather I get interrupted or not
599: }
600:
601: if (i > 20)
602: throw StandardException.newException(
603: SQLState.LOG_FULL, sfe);
604: }
605: }
606: }
607:
608: /**
609: The database is being marked corrupted, get rid of file pointer without
610: writing out anything more.
611: */
612: public void corrupt() throws IOException {
613: synchronized (logFileSemaphore) {
614: if (log != null)
615: log.close();
616: }
617: }
618:
619: public void close() throws IOException, StandardException {
620: if (SanityManager.DEBUG) {
621: if (currentBuffer.position != checksumLogRecordSize)
622: SanityManager
623: .THROWASSERT("Log file being closed with data still buffered "
624: + currentBuffer.position
625: + " "
626: + currentBuffer.bytes_free);
627: }
628:
629: flushLogAccessFile();
630:
631: synchronized (logFileSemaphore) {
632: if (log != null)
633: log.close();
634: }
635: }
636:
637: /* write to the log file */
638: private void writeToLog(byte b[], int off, int len)
639: throws IOException {
640: synchronized (logFileSemaphore) {
641: if (log != null) {
642:
643: // Try to handle case where user application is throwing
644: // random interrupts at cloudscape threads, retry in the case
645: // of IO exceptions 5 times. After that hope that it is
646: // a real disk problem - an IO error in a write to the log file
647: // is going to take down the whole system, so seems worthwhile
648: // to retry.
649: for (int i = 0;; i++) {
650: try {
651: log.write(b, off, len);
652: break;
653: } catch (IOException ioe) {
654: // just fall through and rety the log write 1st 5 times.
655:
656: if (i >= 5)
657: throw ioe;
658: }
659: }
660: }
661: }
662:
663: if (SanityManager.DEBUG) {
664: mon_numWritesToLog++;
665: mon_numBytesToLog += len;
666: }
667: }
668:
669: /**
670: * reserve the space for the checksum log record in the log file.
671: *
672: * @param length the length of the log record to be written
673: * @param logFileNumber current log file number
674: * @param currentPosition current position in the log file.
675: *
676: * @return the space that is needed to write a checksum log record.
677: */
678: protected long reserveSpaceForChecksum(int length,
679: long logFileNumber, long currentPosition)
680: throws StandardException, IOException {
681:
682: int total_log_record_length = length
683: + LOG_RECORD_FIXED_OVERHEAD_SIZE;
684: boolean reserveChecksumSpace = false;
685:
686: /* checksum log record is calculated for a group of log
687: * records that can fit in to a single buffer or for
688: * a single record when it does not fit into
689: * a fit into a buffer at all. When a new buffer
690: * is required to write a log record, log space
691: * has to be reserved before writing the log record
692: * becuase checksum is written in the before the
693: * log records that are being checksummed.
694: * What it also means is a real log instant has to be
695: * reserved for writing the checksum log record in addition
696: * to the log buffer space.
697: */
698:
699: /* reserve checkum space for new log records if a log buffer switch had
700: * happened before because of a explicit log flush requests(like commit)
701: * or a long record write
702: */
703: if (currentBuffer.position == checksumLogRecordSize) {
704: // reserver space if log checksum feature is enabled.
705: reserveChecksumSpace = writeChecksum;
706: } else {
707: if (total_log_record_length > currentBuffer.bytes_free) {
708: // the log record that is going to be written is not
709: // going to fit in the current buffer, switch the
710: // log buffer to create buffer space for it.
711: switchLogBuffer();
712: // reserve space if log checksum feature is enabled.
713: reserveChecksumSpace = writeChecksum;
714: }
715: }
716:
717: if (reserveChecksumSpace) {
718: if (SanityManager.DEBUG) {
719: // Prevoiusly reserved real checksum instant should have been
720: // used, before an another one is generated.
721: SanityManager.ASSERT(checksumInstant == -1,
722: "CHECKSUM INSTANT IS GETTING OVER WRITTEN");
723: }
724:
725: checksumInstant = LogCounter.makeLogInstantAsLong(
726: logFileNumber, currentPosition);
727: return checksumLogRecordSize;
728: } else {
729: return 0;
730: }
731: }
732:
733: /*
734: * generate the checkum log record and write it into the log buffer.
735: */
736: private void writeChecksumLogRecord() throws IOException,
737: StandardException {
738:
739: byte[] b = currentBuffer.buffer;
740: int p = 0; //checksum is written in the beginning of the buffer
741:
742: // writeInt(length)
743: p = writeInt(checksumLength, b, p);
744:
745: // writeLong(instant)
746: p = writeLong(checksumInstant, b, p);
747:
748: //write the checksum log operation
749: logOutputBuffer.setData(b);
750: logOutputBuffer.setPosition(p);
751: logicalOut.writeObject(checksumLogRecord);
752:
753: if (databaseEncrypted) {
754: //encrypt the checksum log operation part.
755: int len = logFactory.encrypt(b, LOG_RECORD_HEADER_SIZE,
756: checksumLength, b, LOG_RECORD_HEADER_SIZE);
757:
758: if (SanityManager.DEBUG)
759: SanityManager
760: .ASSERT(len == checksumLength,
761: "encrypted log buffer length != log buffer len");
762: }
763:
764: p = LOG_RECORD_HEADER_SIZE + checksumLength;
765:
766: // writeInt(length) trailing
767: p = writeInt(checksumLength, b, p);
768:
769: if (SanityManager.DEBUG) {
770: SanityManager.ASSERT(p == checksumLogRecordSize,
771: "position=" + p + "ckrecordsize="
772: + checksumLogRecordSize);
773: if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG)) {
774: SanityManager.DEBUG(LogToFile.DBG_FLAG,
775: "Write log record: tranId=Null"
776: + " instant: "
777: + LogCounter
778: .toDebugString(checksumInstant)
779: + " length: " + checksumLength + "\n"
780: + checksumLogOperation + "\n");
781: }
782: checksumInstant = -1;
783: }
784:
785: }
786:
787: protected void writeEndMarker(int marker) throws IOException,
788: StandardException {
789: //flush all the buffers and then write the end marker.
790: flushLogAccessFile();
791:
792: byte[] b = currentBuffer.buffer;
793: int p = 0; //end is written in the beginning of the buffer, no
794: //need to checksum a int write.
795: p = writeInt(marker, b, p);
796: writeToLog(b, 0, p);
797: }
798:
799: }
|