📄 logaccessfile.java
字号:
currentBuffer.bytes_free -= len; currentBuffer.position += len; } /** * Write data from all dirty buffers into the log file. * <p> * A call for clients of LogAccessFile to insure that all privately buffered * data has been writen to the file - so that reads on the file using one * of the various scan classes will see * all the data which has been writen to this point. * <p> * Note that this routine only "writes" the data to the file, this does not * mean that the data has been synced to disk unless file was opened in * WRITE SYNC mode(rws/rwd). The only way to insure that is by calling * is to call syncLogAccessFile() after this call in Non-WRITE sync mode(rw) * * <p> * MT-Safe : parallel thereads can call this function, only one threads does * the flush and the other threads waits for the one that is doing the flush to finish. * Currently there are two possible threads that can call this function in parallel * 1) A Thread that is doing the commit * 2) A Thread that is writing to the log and log buffers are full or * a log records does not fit in a buffer. (Log Buffers * full(switchLogBuffer() or a log record size that is greater than * logbuffer size has to be writtern through writeToLog call directlty) * Note: writeToLog() is not synchronized on the semaphore * that is used to do buffer management to allow writes * to the free buffers when flush is in progress. **/ protected void flushDirtyBuffers() throws IOException { LogAccessFileBuffer buf = null; int noOfBuffers; int nFlushed= 0; try{ synchronized(this) { /**if some one else flushing wait, otherwise it is possible * different threads will get different buffers and order can * not be determined. * **/ while(flushInProgress) { try{ wait(); }catch (InterruptedException ie) { //do nothing, let the flush request to complete. //because it possible that other thread which is //currently might have completed this request also , //if exited on interrupt and throw exception, can not //be sure whether this transaction is COMMITTED ot not. } } noOfBuffers = dirtyBuffers.size(); if(noOfBuffers > 0) buf = (LogAccessFileBuffer) dirtyBuffers.removeFirst(); flushInProgress = true; } while(nFlushed < noOfBuffers) { if (buf.position != 0) writeToLog(buf.buffer, 0, buf.position); nFlushed++; synchronized(this) { //add the buffer that was written previosly to the free list freeBuffers.addLast(buf); if(nFlushed < noOfBuffers) buf = (LogAccessFileBuffer) dirtyBuffers.removeFirst(); else { //see if we can flush more, that came when we are at it. //don't flush more than the total number of buffers, //that might lead to starvation of the current thread. int size = dirtyBuffers.size(); if(size > 0 && nFlushed <= LOG_NUMBER_LOG_BUFFERS) { noOfBuffers += size; buf = (LogAccessFileBuffer) dirtyBuffers.removeFirst(); } } } } }finally{ synchronized(this) { flushInProgress = false; notifyAll(); } } } //flush all the the dirty buffers to disk public void flushLogAccessFile() throws IOException, StandardException { switchLogBuffer(); flushDirtyBuffers(); } /** * Appends the current Buffer to the dirty Buffer list and assigns a free * buffer to be the currrent active buffer . Flushing of the buffer * to disk is delayed if there is a free buffer available. * dirty buffers will be flushed to the disk * when flushDirtyBuffers() is invoked by a commit call * or when no more free buffers are available. */ public void switchLogBuffer() throws IOException, StandardException { synchronized(this) { // ignore empty buffer switch requests if(currentBuffer.position == checksumLogRecordSize) return; // calculate the checksum for the current log buffer // and write the record to the space reserverd in // the beginning of the buffer. if(writeChecksum && !directWrite) { checksumLogOperation.reset(); checksumLogOperation.update(currentBuffer.buffer, checksumLogRecordSize, currentBuffer.position - checksumLogRecordSize); writeChecksumLogRecord(); } //add the current buffer to the flush buffer list dirtyBuffers.addLast(currentBuffer); //if there is No free buffer, flush the buffers to get a free one if(freeBuffers.size() == 0) { flushDirtyBuffers(); //after the flush call there should be a free buffer //because this is only methods removes items from //free buffers and removal is in synchronized block. } // there should be free buffer available at this point. if (SanityManager.DEBUG) SanityManager.ASSERT(freeBuffers.size() > 0); //switch over to the next log buffer, let someone else write it. currentBuffer = (LogAccessFileBuffer) freeBuffers.removeFirst(); currentBuffer.init(checksumLogRecordSize); if (SanityManager.DEBUG) { SanityManager.ASSERT(currentBuffer.position == checksumLogRecordSize); SanityManager.ASSERT( currentBuffer.bytes_free == currentBuffer.length); SanityManager.ASSERT(currentBuffer.bytes_free > 0); } } } /** * Guarantee all writes up to the last call to flushLogAccessFile on disk. * <p> * A call for clients of LogAccessFile to insure that all data written * up to the last call to flushLogAccessFile() are written to disk. * This call will not return until those writes have hit disk. * <p> * Note that this routine may block waiting for I/O to complete so * callers should limit the number of resource held locked while this * operation is called. It is expected that the caller * Note that this routine only "writes" the data to the file, this does not * mean that the data has been synced to disk. The only way to insure that * is to first call switchLogBuffer() and then follow by a call of sync(). * **/ public void syncLogAccessFile() throws IOException, StandardException { for( int i=0; ; ) { // 3311: JVM sync call sometimes fails under high load against NFS // mounted disk. We re-try to do this 20 times. try { synchronized( this) { log.sync( false); } // the sync succeed, so return break; } catch( SyncFailedException sfe ) { i++; try { // wait for .2 of a second, hopefully I/O is done by now // we wait a max of 4 seconds before we give up Thread.sleep( 200 ); } catch( InterruptedException ie ) { //does not matter weather I get interrupted or not } if( i > 20 ) throw StandardException.newException( SQLState.LOG_FULL, sfe, null); } } } /** The database is being marked corrupted, get rid of file pointer without writing out anything more. */ public void corrupt() throws IOException { synchronized(logFileSemaphore) { if (log != null) log.close(); } } public void close() throws IOException, StandardException { if (SanityManager.DEBUG) { if (currentBuffer.position != checksumLogRecordSize) SanityManager.THROWASSERT( "Log file being closed with data still buffered " + currentBuffer.position + " " + currentBuffer.bytes_free); } flushLogAccessFile(); synchronized(logFileSemaphore) { if (log != null) log.close(); } } /* write to the log file */ private void writeToLog(byte b[], int off, int len) throws IOException { synchronized(logFileSemaphore) { if (log != null) { // Try to handle case where user application is throwing // random interrupts at cloudscape threads, retry in the case // of IO exceptions 5 times. After that hope that it is // a real disk problem - an IO error in a write to the log file // is going to take down the whole system, so seems worthwhile // to retry. for (int i = 0; ;i++) { try { log.write(b, off, len); break; } catch (IOException ioe) { // just fall through and rety the log write 1st 5 times. if (i >= 5) throw ioe; } } } } if (SanityManager.DEBUG) { mon_numWritesToLog++; mon_numBytesToLog += len; } } /** * reserve the space for the checksum log record in the log file. * * @param length the length of the log record to be written * @param logFileNumber current log file number * @param currentPosition current position in the log file. * * @return the space that is needed to write a checksum log record. */ protected long reserveSpaceForChecksum(int length, long logFileNumber, long currentPosition ) throws StandardException, IOException { int total_log_record_length = length + LOG_RECORD_FIXED_OVERHEAD_SIZE; boolean reserveChecksumSpace = false; /* checksum log record is calculated for a group of log * records that can fit in to a single buffer or for * a single record when it does not fit into * a fit into a buffer at all. When a new buffer * is required to write a log record, log space * has to be reserved before writing the log record * becuase checksum is written in the before the * log records that are being checksummed. * What it also means is a real log instant has to be * reserved for writing the checksum log record in addition * to the log buffer space. */ /* reserve checkum space for new log records if a log buffer switch had * happened before because of a explicit log flush requests(like commit) * or a long record write */ if(currentBuffer.position == checksumLogRecordSize) { // reserver space if log checksum feature is enabled. reserveChecksumSpace = writeChecksum; } else{ if (total_log_record_length > currentBuffer.bytes_free) { // the log record that is going to be written is not // going to fit in the current buffer, switch the // log buffer to create buffer space for it. switchLogBuffer(); // reserve space if log checksum feature is enabled. reserveChecksumSpace = writeChecksum; } } if(reserveChecksumSpace) { if (SanityManager.DEBUG) { // Prevoiusly reserved real checksum instant should have been // used, before an another one is generated. SanityManager.ASSERT(checksumInstant == -1, "CHECKSUM INSTANT IS GETTING OVER WRITTEN"); } checksumInstant = LogCounter.makeLogInstantAsLong(logFileNumber, currentPosition); return checksumLogRecordSize; }else { return 0 ; } } /* * generate the checkum log record and write it into the log buffer. */ private void writeChecksumLogRecord() throws IOException, StandardException { byte[] b = currentBuffer.buffer; int p = 0; //checksum is written in the beginning of the buffer // writeInt(length) p = writeInt(checksumLength, b , p); // writeLong(instant) p = writeLong(checksumInstant, b , p); //write the checksum log operation logOutputBuffer.setData(b); logOutputBuffer.setPosition(p); logicalOut.writeObject(checksumLogRecord); if(databaseEncrypted) { //encrypt the checksum log operation part. int len = logFactory.encrypt(b, LOG_RECORD_HEADER_SIZE, checksumLength, b, LOG_RECORD_HEADER_SIZE); if (SanityManager.DEBUG) SanityManager.ASSERT(len == checksumLength, "encrypted log buffer length != log buffer len"); } p = LOG_RECORD_HEADER_SIZE + checksumLength ; // writeInt(length) trailing p = writeInt(checksumLength, b, p ); if (SanityManager.DEBUG) { SanityManager.ASSERT(p == checksumLogRecordSize, "position=" + p + "ckrecordsize=" + checksumLogRecordSize); if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG)) { SanityManager.DEBUG( LogToFile.DBG_FLAG, "Write log record: tranId=Null" + " instant: " + LogCounter.toDebugString(checksumInstant) + " length: " + checksumLength + "\n" + checksumLogOperation + "\n"); } checksumInstant = -1; } } protected void writeEndMarker(int marker) throws IOException, StandardException { //flush all the buffers and then write the end marker. flushLogAccessFile(); byte[] b = currentBuffer.buffer; int p = 0; //end is written in the beginning of the buffer, no //need to checksum a int write. p = writeInt(marker , b , p); writeToLog(b, 0, p); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -