EMMA Coverage Report (generated Wed Jun 28 22:15:27 PDT 2006)
[all classes][org.apache.derby.impl.store.raw.log]

COVERAGE SUMMARY FOR SOURCE FILE [LogAccessFile.java]

nameclass, %method, %block, %line, %
LogAccessFile.java100% (1/1)100% (18/18)91%  (1039/1141)91%  (209.2/230)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class LogAccessFile100% (1/1)100% (18/18)91%  (1039/1141)91%  (209.2/230)
LogAccessFile (LogToFile, StorageRandomAccessFile, int): void 100% (1/1)97%  (143/148)94%  (32/34)
close (): void 100% (1/1)51%  (23/45)80%  (6.4/8)
corrupt (): void 100% (1/1)75%  (15/20)87%  (4.4/5)
flushDirtyBuffers (): void 100% (1/1)83%  (100/121)91%  (29/32)
flushLogAccessFile (): void 100% (1/1)100% (5/5)100% (3/3)
reserveSpaceForChecksum (int, long, long): long 100% (1/1)98%  (48/49)100% (11.9/12)
switchLogBuffer (): void 100% (1/1)92%  (97/106)96%  (17.2/18)
syncLogAccessFile (): void 100% (1/1)43%  (15/35)41%  (5.7/14)
write (byte [], int, int): void 100% (1/1)97%  (33/34)99%  (5/5)
write (int): void 100% (1/1)97%  (29/30)99%  (4/4)
writeChecksumLogRecord (): void 100% (1/1)98%  (117/119)99%  (16.9/17)
writeEndMarker (int): void 100% (1/1)100% (20/20)100% (6/6)
writeInt (int): void 100% (1/1)97%  (28/29)99%  (4/4)
writeInt (int, byte [], int): int 100% (1/1)100% (40/40)100% (5/5)
writeLogRecord (int, long, byte [], int, byte [], int, int): void 100% (1/1)100% (181/181)100% (38/38)
writeLong (long): void 100% (1/1)97%  (28/29)99%  (4/4)
writeLong (long, byte [], int): int 100% (1/1)100% (88/88)100% (9/9)
writeToLog (byte [], int, int): void 100% (1/1)69%  (29/42)66%  (7.9/12)

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

[all classes][org.apache.derby.impl.store.raw.log]
EMMA 2.0.5312 (C) Vladimir Roubtsov