📄 logaccessfile.java
字号:
/* Derby - Class org.apache.derby.impl.store.raw.log.LogAccessFile Copyright 1999, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derby.impl.store.raw.log;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.io.StorageRandomAccessFile;import java.io.IOException;import java.io.OutputStream;import java.io.SyncFailedException;import java.io.InterruptedIOException;import java.util.LinkedList;import org.apache.derby.iapi.services.io.FormatIdOutputStream;import org.apache.derby.iapi.services.io.ArrayOutputStream;import org.apache.derby.iapi.store.raw.RawStoreFactory;/** Wraps a RandomAccessFile file to provide buffering on log writes. Only supports the write calls required for the log! MT - unsafe. Caller of this class must provide synchronization. The one exception is with the log file access, LogAccessFile will touch the log only inside synchronized block protected by the semaphore, which is defined by the creator of this object. Write to the log buffers are allowed when there are free buffers even when dirty buffers are being written(flushed) to the disk by a different thread. Only one flush writes to log file at a time, other wait for it to finish. Except for flushLogAccessFile , SyncAccessLogFile other function callers must provide syncronization that will allow only one of them to write to the buffers. Log Buffers are used in circular fashion, each buffer moves through following stages: freeBuffers --> dirtyBuffers --> freeBuffers. Movement of buffers from one stage to another stage is synchronized using the object(this) of this class. A Checksum log record that has the checksum value for the data that is being written to the disk is generated and written before the actual data. Except for the large log records that does not fit into a single buffer, checksum is calcualted for a group of log records that are in the buffer when buffers is switched. Checksum log record is written into the reserved space in the beginning buffer. In case of a large log record that does not fit into a bufffer, it needs to be written directly to the disk instead of going through the log buffers. In this case the log record write gets broken into three parts: 1) Write checksum log record and LOG RECORD HEADER (length + instant) 2) Write the log record. 3) Write the trailing length of the log record. Checksum log records helps in identifying the incomplete log disk writes during recovery. This is done by recalculating the checksum value for the data on the disk and comparing it to the the value stored in the checksum log record. */public class LogAccessFile { /** * The fixed size of a log record is 16 bytes: * int length : 4 bytes * long instant : 8 bytes * int trailing length : 4 bytes **/ private static final int LOG_RECORD_FIXED_OVERHEAD_SIZE = 16; private static final int LOG_RECORD_HEADER_SIZE = 12; //(length + instant) private static final int LOG_RECORD_TRAILER_SIZE = 4; //trailing length private static final int LOG_NUMBER_LOG_BUFFERS = 3; private LinkedList freeBuffers; //list of free buffers private LinkedList dirtyBuffers; //list of dirty buffers to flush private LogAccessFileBuffer currentBuffer; //current active buffer private boolean flushInProgress = false; private final StorageRandomAccessFile log; // log can be touched only inside synchronized block protected by // logFileSemaphore. private final Object logFileSemaphore; static int mon_numWritesToLog; static int mon_numBytesToLog; //streams used to generated check sume log record ; see if there is any simpler way private ArrayOutputStream logOutputBuffer; private FormatIdOutputStream logicalOut; private boolean directWrite = false; //true when log is written directly to file. private long checksumInstant = -1; private int checksumLength; private int checksumLogRecordSize; //checksumLength + LOG_RECORD_FIXED_OVERHEAD_SIZE private boolean writeChecksum; private ChecksumOperation checksumLogOperation; private LogRecord checksumLogRecord; private LogToFile logFactory; private boolean databaseEncrypted=false; public LogAccessFile(LogToFile logFactory, StorageRandomAccessFile log, int bufferSize) { if (SanityManager.DEBUG) { if(SanityManager.DEBUG_ON("LogBufferOff")) bufferSize = 10; // make it very tiny } this.log = log; logFileSemaphore = log; this.logFactory = logFactory; if (SanityManager.DEBUG) SanityManager.ASSERT(LOG_NUMBER_LOG_BUFFERS >= 1); //initialize buffers lists freeBuffers = new LinkedList(); dirtyBuffers = new LinkedList(); //add all buffers to free list for (int i = 0; i < LOG_NUMBER_LOG_BUFFERS; i++) { LogAccessFileBuffer b = new LogAccessFileBuffer(bufferSize); freeBuffers.addLast(b); } currentBuffer = (LogAccessFileBuffer) freeBuffers.removeFirst(); // Support for Transaction Log Checksum in Derby was added in 10.1 // Check to see if the Store have been upgraded to 10.1 or later before // writing the checksum log records. Otherwise recovery will fail // incase user tries to revert back to versions before 10.1 in // soft upgrade mode. writeChecksum = logFactory.checkVersion(RawStoreFactory.DERBY_STORE_MAJOR_VERSION_10, RawStoreFactory.DERBY_STORE_MINOR_VERSION_1); if(writeChecksum) { /** * setup structures that are required to write the checksum log records * for a group of log records are being written to the disk. */ checksumLogOperation = new ChecksumOperation(); checksumLogOperation.init(); checksumLogRecord = new LogRecord(); // Note: Checksum log records are not related any particular transaction, // they are written to store a checksum information identify // incomplete log record writes. No transacton id is set for this // log record. That is why a null argument is passed below // setValue(..) call. checksumLogRecord.setValue(null, checksumLogOperation); checksumLength = checksumLogRecord.getStoredSize(checksumLogOperation.group(), null) + checksumLogOperation.getStoredSize(); // calculate checksum log operation length when the database is encrypted if (logFactory.databaseEncrypted()) { checksumLength = logFactory.getEncryptedDataLength(checksumLength); databaseEncrypted = true; } checksumLogRecordSize = checksumLength + LOG_RECORD_FIXED_OVERHEAD_SIZE; //streams required to convert a log record to raw byte array. logOutputBuffer = new ArrayOutputStream(); logicalOut = new FormatIdOutputStream(logOutputBuffer); /** initialize the buffer with space reserved for checksum log record in * the beginning of the log buffer; checksum record is written into * this space when buffer is switched or while doing direct write to the log file. */ }else { //checksumming of transaction log feature is not in use. checksumLogRecordSize = 0; } currentBuffer.init(checksumLogRecordSize); } private byte[] db = new byte[LOG_RECORD_TRAILER_SIZE]; /** * Write a single log record to the stream. * <p> * For performance pass all parameters rather into a specialized routine * rather than maintaining the writeInt, writeLong, and write interfaces * that this class provides as a standard OutputStream. It will make it * harder to use other OutputStream implementations, but makes for less * function calls and allows optimizations knowing when to switch buffers. * <p> * This routine handles all log records which are smaller than one log * buffer. If a log record is bigger than a log buffer it calls * writeUnbufferedLogRecord(). * <p> * The log record written will always look the same as if the following * code had been executed: * writeInt(length) * writeLong(instant) * write(data, data_offset, (length - optional_data_length) ) * * if (optional_data_length != 0) * write(optional_data, optional_data_offset, optional_data_length) * * writeInt(length) * * @param length (data + optional_data) length bytes to write * @param instant the log address of this log record. * @param data "from" array to copy "data" portion of rec * @param data_offset offset in "data" to start copying from. * @param optional_data "from" array to copy "optional data" from * @param optional_data_offset offset in "optional_data" to start copy from * @param optional_data_length length of optional data to copy. * * @exception StandardException Standard exception policy. **/ public void writeLogRecord( int length, long instant, byte[] data, int data_offset, byte[] optional_data, int optional_data_offset, int optional_data_length) throws StandardException, IOException { int total_log_record_length = length + LOG_RECORD_FIXED_OVERHEAD_SIZE; if (total_log_record_length <= currentBuffer.bytes_free) { byte[] b = currentBuffer.buffer; int p = currentBuffer.position; // writeInt(length) p = writeInt(length, b, p); // writeLong(instant) p = writeLong(instant, b , p); // write(data, data_offset, length - optional_data_length) int transfer_length = (length - optional_data_length); System.arraycopy(data, data_offset, b, p, transfer_length); p += transfer_length; if (optional_data_length != 0) { // write( // optional_data, optional_data_offset, optional_data_length); System.arraycopy( optional_data, optional_data_offset, b, p, optional_data_length); p += optional_data_length; } // writeInt(length) p = writeInt(length, b, p); currentBuffer.position = p; currentBuffer.bytes_free -= total_log_record_length; } else { /** Because current log record will never fit in a single buffer * a direct write to the log file is required instead of * writing the log record through the log bufffers. */ directWrite = true; byte[] b = currentBuffer.buffer; int p = currentBuffer.position; // writeInt(length) p = writeInt(length , b, p); // writeLong(instant) p = writeLong(instant, b, p); currentBuffer.position = p; currentBuffer.bytes_free -= LOG_RECORD_HEADER_SIZE; /** using a seperate small buffer to write the traling length * instead of the log buffer because data portion will be * written directly to log file after the log buffer is * flushed and the trailing length should be written after that. */ // writeInt(length) writeInt(length , db, 0); if(writeChecksum) { checksumLogOperation.reset(); checksumLogOperation.update(b, checksumLogRecordSize, p - checksumLogRecordSize); checksumLogOperation.update(data, data_offset, length - optional_data_length); if (optional_data_length != 0) { checksumLogOperation.update(optional_data, optional_data_offset, optional_data_length); } // update the checksum to include the trailing length. checksumLogOperation.update(db, 0, LOG_RECORD_TRAILER_SIZE); // write checksum log record to the log buffer writeChecksumLogRecord(); } // now do the writes directly to the log file. // flush all buffers before wrting directly to the log file. flushLogAccessFile(); // Note:No Special Synchronization required here , // There will be nothing to write by flushDirtyBuffers that can run // in parallel to the threads that is executing this code. Above // flush call should have written all the buffers and NO new log will // get added until the following direct log to file call finishes. // write the rest of the log directltly to the log file. writeToLog(data, data_offset, length - optional_data_length); if (optional_data_length != 0) { writeToLog( optional_data, optional_data_offset, optional_data_length); } // write the trailing length writeToLog(db,0, 4); directWrite = false; } } private final int writeInt(int i , byte b[], int p) { b[p++] = (byte) ((i >>> 24) & 0xff); b[p++] = (byte) ((i >>> 16) & 0xff); b[p++] = (byte) ((i >>> 8) & 0xff); b[p++] = (byte) (i & 0xff); return p; } private final int writeLong(long l , byte b[], int p) { b[p++] = (byte) (((int)(l >>> 56)) & 0xff); b[p++] = (byte) (((int)(l >>> 48)) & 0xff); b[p++] = (byte) (((int)(l >>> 40)) & 0xff); b[p++] = (byte) (((int)(l >>> 32)) & 0xff); b[p++] = (byte) (((int)(l >>> 24)) & 0xff); b[p++] = (byte) (((int)(l >>> 16)) & 0xff); b[p++] = (byte) (((int)(l >>> 8)) & 0xff); b[p++] = (byte) (((int)l) & 0xff); return p; } public void writeInt(int i) { if (SanityManager.DEBUG) { SanityManager.ASSERT(currentBuffer.bytes_free >= 4); } currentBuffer.position = writeInt(i , currentBuffer.buffer, currentBuffer.position); currentBuffer.bytes_free -= 4; } public void writeLong(long l) { if (SanityManager.DEBUG) { SanityManager.ASSERT(currentBuffer.bytes_free >= 8); } currentBuffer.position = writeLong(l , currentBuffer.buffer, currentBuffer.position); currentBuffer.bytes_free -= 8; } public void write(int b) { if (SanityManager.DEBUG) { SanityManager.ASSERT(currentBuffer.bytes_free > 0); } currentBuffer.buffer[currentBuffer.position++] = (byte) b; currentBuffer.bytes_free--; } public void write(byte b[], int off, int len) { if (SanityManager.DEBUG) { SanityManager.ASSERT(len <= currentBuffer.bytes_free); } System.arraycopy(b, off, currentBuffer.buffer, currentBuffer.position, len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -