KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > sql > file > Log1


1 package com.quadcap.sql.file;
2
3 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.io.File JavaDoc;
42 import java.io.FileOutputStream JavaDoc;
43 import java.io.IOException JavaDoc;
44 import java.io.RandomAccessFile JavaDoc;
45
46 import java.util.ArrayList JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.Map JavaDoc;
49 import java.util.Properties JavaDoc;
50
51 import javax.concurrent.BoundedBuffer;
52 import javax.concurrent.Channel;
53 import javax.concurrent.Latch;
54
55 import com.quadcap.sql.lock.Transaction;
56 import com.quadcap.sql.lock.TransactionObserver;
57
58 import com.quadcap.util.collections.LongMap;
59
60 import com.quadcap.util.ConfigNumber;
61 import com.quadcap.util.Debug;
62 import com.quadcap.util.Util;
63
64 /**
65  * Rolling write-ahead log implementation. Checkpoints plus physical logging
66  * of block writes is coupled with logical logging to support transactions
67  * rollback and recovery.
68  *
69  * @author Stan Bailes
70  */

71 public class Log1 implements Log {
72     /** the database we're logging for */
73     private Datafile db;
74
75     /** the database file we're logging for */
76     private BlockFile dbFile;
77
78     /** Log writer/reader */
79     Logger logger;
80
81     /** log file directory */
82     private File JavaDoc dbRootDir;
83
84     /** log sync thread */
85     LogSync logSync;
86
87     /** log sync thread done, close interlock */
88     Latch closeLatch = new Latch();
89
90     /** producer/consumer conduit */
91     Channel channel = new BoundedBuffer(2048);
92
93     /** our datatbase's file lock */
94     Object JavaDoc fileLock;
95
96     /** Map rows during recovery, null otherwise. */
97     LongMap rowIdMap = new LongMap(256);
98
99     /** "before-images" file */
100     FileOutputStream JavaDoc bfo;
101     boolean bfoActive = false;
102
103     boolean recovering = false;
104
105     int blockSize;
106
107     /*{com.quadcap.util.Config-vars.xml-1020}
108      * <config-var>
109      * <config-name>qed.minSyncInterval</config-name>
110      * <config-dflt>15000 (15 seconds)</config-dflt>
111      * <config-desc>The minimum interval (in ms) between database
112      * sync operations.</config-desc>
113      * </config-var>
114      */

115     long minSyncInterval
116     = ConfigNumber.find("qed.minSyncInterval", "15000").longValue();
117
118     /*{com.quadcap.util.Config-vars.xml-1021}
119      * <config-var>
120      * <config-name>qed.maxSyncInterval</config-name>
121      * <config-dflt>60000 (60 seconds)</config-dflt>
122      * <config-desc>The maximum interval (in ms) between database
123      * sync operations. Use this to help
124      * limit the logfile, scratch, and before-images files.
125      * </config-desc>
126      * </config-var>
127      */

128     long maxSyncInterval
129     = ConfigNumber.find("qed.maxSyncInterval", "60000").longValue();
130
131     int syncMap = 0 +
132     (1 << 1) + // flush -> before.sync
133
(1 << 2) + // flush -> logger.sync
134
// (1 << 3) + // flush -> db.tempFile.flush()
135
(1 << 4) + // checkpoint -> dbfile.flush(fastSync)
136
(1 << 5) + // checkpoint -> logger.sync
137
(1 << 6) + // checkpoint -> db.checkpoint(fastSync)
138
(1 << 7) + // save block -> store.sync
139
0;
140     
141     //#ifdef DEBUG
142
String JavaDoc[] syncStrs = {
143         "Log1.reallyFlush from Log2.flushLog",
144         "Log1.reallyFlush: bfo.sync()",
145         "Log1.reallyFlush: logger.sync()",
146         "Log1.reallyFlush: db.tempFile.flush(!)",
147         "Log1.reallyCheckpoint: dbfile.flush(!)",
148         "Log1.reallyCheckpoint: logger.sync()",
149         "Log1.reallyCheckpoint: dbfile.checkpoint(!)",
150         "Log1.saveBlock: bfo.sync"
151     };
152
153     int[] syncCnts = new int[syncStrs.length];
154     //#endif
155
protected boolean checksync(int x) {
156         //#ifdef DEBUG
157
syncCnts[x]++;
158         //Debug.println("checkSync[" + ret + ": " + syncCnts[x] + " - " + syncStrs[x]);
159
//#endif
160
boolean ret = (syncMap & (1 << x)) != 0;
161         return ret;
162     }
163
164     /**
165       * Constructor for transaction log
166       *
167       */

168     public Log1() {}
169
170     /**
171      * Initialize the log
172      *
173      * @param db the underlying database that we're logging for.
174      * @param create true if we're creating this database from scratch,
175      * in which case we can skip any recovery-related activity
176      */

177     public void init(Datafile db, boolean create, Properties JavaDoc props)
178     throws IOException JavaDoc
179     {
180         this.db = db;
181         this.dbFile = db.file;
182         this.blockSize = dbFile.getBlockSize();
183     this.dbRootDir = db.dbRootDir;
184         this.fileLock = db.getFileLock();
185         /*{com.quadcap.sql.Datafile-conn.xml-23}
186          * <config-var>
187          * <config-name>fastSync</config-name>
188          * <config-dflt>true</config-dflt>
189          * <config-desc>If <code>true</code>, omit time-consuming
190          * sync operations to permit greater throughput.
191          * </config-desc>
192          * </config-var>
193          */

194 // this.fastSync =
195
// props.getProperty("fastSync",
196
// String.valueOf(fastSync)).equalsIgnoreCase("true");
197

198         /*{com.quadcap.sql.Datafile-conn.xml-25}
199          * <config-var>
200          * <config-name>minSyncInterval</config-name>
201          * <config-dflt>from config:qed.minSyncInterval</config-dflt>
202          * <config-desc>Minimum interval between syncs. Set to zero to
203          * ensure a sync after every transaction.</config-desc>
204          * </config-var>
205          */

206         this.minSyncInterval =
207             Long.parseLong(props.getProperty("minSyncInterval",
208                                              String.valueOf(minSyncInterval)));
209
210         /*{com.quadcap.sql.Datafile-conn.xml-26}
211          * <config-var>
212          * <config-name>maxSyncInterval</config-name>
213          * <config-dflt>from config:qed.maxSyncInterval</config-dflt>
214          * <config-desc>Maximum interval between syncs. Use this to help
215          * limit the logfile, scratch, and before-images files.
216          * </config-desc>
217          * </config-var>
218          */

219         this.maxSyncInterval =
220             Long.parseLong(props.getProperty("maxSyncInterval",
221                                              String.valueOf(maxSyncInterval)));
222
223         // Gonna leave this undocumented for now since there's only
224
// one instance of this class.... Too many knobs.
225
String JavaDoc loggerClass = props.getProperty("loggerClass");
226         try {
227             this.logger = (Logger)(Class.forName(loggerClass).newInstance());
228         } catch (Throwable JavaDoc t) {
229             this.logger = new Logger1();
230         }
231         logger.init(this, create, props);
232
233     }
234
235     public void start() {
236         logSync = new LogSync(this);
237         logSync.setDaemon(true);
238         logSync.start();
239     }
240
241     public void remove() throws IOException JavaDoc {
242         new File JavaDoc(dbRootDir, "logfile").delete();
243     }
244
245     /**
246      * Return the database that we're logging for
247      */

248     public Datafile getDatafile() { return db; }
249
250     /**
251      * Return the database root directory
252      */

253     public File JavaDoc getDbRootDir() { return dbRootDir; }
254
255     /**
256      * Add a transaction's log record to the end of the open log file
257      */

258     int pendingBegins = 0;
259     public void addEntry(LogEntry entry)
260     throws IOException JavaDoc
261     {
262         if (entry.getCode() == LogEntry.BEGIN_TRANSACTION) {
263             pendingBegins++;
264         }
265         put(entry);
266     }
267     
268     /**
269      * Flush and close the log file.
270      */

271     public void close() throws IOException JavaDoc {
272         put(opClose);
273         try {
274             closeLatch.acquire();
275         } catch (InterruptedException JavaDoc ex) {
276         }
277     }
278     
279     /**
280      * Flush all log records to disk. Action not performed on this
281      * thread, we're in a hurry....
282      */

283     public void flushLog() throws IOException JavaDoc {
284         put(opFlush);
285     }
286
287     /**
288      * Perform a checkpoint operation.
289      */

290     public void checkpoint() throws IOException JavaDoc {
291         put(opCheckpoint);
292     }
293
294     /**
295      * Wait for all queued ops to be processed by the log sync thread
296      */

297     public void sync() throws IOException JavaDoc {
298         Latch latch = new Latch();
299         put(new Sync(latch));
300         try {
301             latch.acquire();
302         } catch (InterruptedException JavaDoc ex) {
303         }
304     }
305
306     /**
307      * Transaction rollback.
308      */

309     public void rollbackTransaction(Transaction trans)
310     throws IOException JavaDoc
311     {
312         put(new Rollback(trans));
313         sync();
314     }
315         
316     /**
317      * Statement rollback.
318      */

319     public void rollbackStatement(Transaction trans, int stmtId)
320     throws IOException JavaDoc
321     {
322         put(new Rollback(trans, stmtId));
323         sync();
324     }
325
326     /**
327      * Restart from a previous state
328      */

329     public void restart() throws Exception JavaDoc {
330         reallyRestart();
331     }
332
333     //------------------------------------------------------------------
334

335     /**
336      * Retrieve a row mapping.
337      */

338     public final long getRowMap(long rowId) {
339     long ret = rowId;
340     if (rowIdMap != null) {
341         Long JavaDoc mapped = (Long JavaDoc)rowIdMap.get(rowId);
342         if (mapped != null) ret = mapped.longValue();
343     }
344     return ret;
345     }
346
347     /**
348      * Remember a row mapping {old,new} The old row (logRow) is now stored
349      * in a new place (fileRow), so any stored log entries that refer to
350      * the old row need to be translated to use the new row.
351      *
352      * @param logRow the "old" row
353      * @param fileRow the "new" row
354      */

355     public final void putRowMap(long logRow, long fileRow) {
356         if (rowIdMap == null) rowIdMap = new LongMap(256);
357     rowIdMap.put(logRow, new Long JavaDoc(fileRow));
358     }
359
360     public final void removeRowMap(long row) {
361         if (rowIdMap != null) rowIdMap.remove(row);
362     }
363
364     void reallyFlush() throws IOException JavaDoc {
365         //#ifdef DEBUG
366
if (Trace.bit(18)) {
367             Debug.println(this + ".reallyFlush()");
368         }
369         //#endif
370
if (checksync(2)) logger.sync();
371         if (db.tempFile != null) {
372             db.tempFile.flush(!checksync(3));
373         }
374     }
375     
376     void reallyClose() throws IOException JavaDoc {
377         try {
378             if (logSync != null) {
379                 logSync.close();
380             }
381         } finally {
382             logSync = null;
383             if (bfo != null) {
384                 try {
385                     bfo.close();
386                 } finally {
387                     bfo = null;
388                 }
389             }
390         }
391     }
392
393     void reallyRollbackTransaction(Transaction tr) throws Exception JavaDoc {
394         //#ifdef DEBUG
395
if (Trace.bit(18)) {
396             Debug.println("reallyRollbackTransaction(" + tr + ")");
397         }
398         //#endif
399
long t = tr.getTransactionId();
400         LogEntry e = logger.getLastOp(t);
401         try {
402             while (e != null) {
403                 //Debug.println(" e = " + e);
404
if (e.getTransactionId() == t) {
405                     switch (e.getCode()) {
406                     case LogEntry.STEP:
407                         if (e.getRedoState() == LogEntry.DONE) {
408                             try {
409                                 e.undo(tr, db);
410                             } catch (Throwable JavaDoc th) {
411                                 //#ifdef DEBUG
412
Debug.println("Exception in rollback transaction");
413                                 Debug.print(th);
414                                 //#endif
415
}
416                             logger.setRedoState(e, LogEntry.UNDONE);
417                         }
418                         break;
419                     case LogEntry.BEGIN_TRANSACTION:
420                         e = null;
421                         break;
422                     default:
423                         break;
424                     }
425                 }
426                 e = (e == null || e.getPrev() < 0) ? null : logger.getPrevOp(e);
427             }
428         } finally {
429             rowIdMap = null;
430         }
431     }
432     
433     void reallyRollbackStatement(Transaction tr, int s) throws Exception JavaDoc {
434         long t = tr.getTransactionId();
435         LogEntry e = logger.getLastOp(t);
436         rowIdMap = null;
437         try {
438             while (e != null) {
439                 if (e.getStatementId() == s) {
440                     switch (e.getCode()) {
441                     case LogEntry.STEP:
442                         if (e.getRedoState() == LogEntry.DONE) {
443                             try {
444                                 e.undo(tr, db);
445                             } catch (Throwable JavaDoc th) {
446                                 //#ifdef DEBUG
447
Debug.println("Exception in statement rollback");
448                                 Debug.print(th);
449                                 //#endif
450
}
451                             logger.setRedoState(e, LogEntry.UNDONE);
452                         }
453                         break;
454                     case LogEntry.BEGIN_STATEMENT:
455                         //Debug.println("--- end rollbackStatement(" + tr + "," +
456
// s + ")");
457
return;
458                     default:
459                         break;
460                     }
461                 }
462                 e = logger.getPrevOp(e);
463             }
464         } finally {
465             rowIdMap = null;
466         }
467     }
468     
469     LogEntry scanLog(LongMap t) throws IOException JavaDoc {
470         LogEntry op = logger.getFirstOp();
471         LogEntry last = op;
472         for (; op != null; last = op, op = logger.getNextOp()) {
473             LogEntry e = op;
474             if (e != null && e.getCode() == LogEntry.COMMIT) {
475                 //Debug.println(" [T:" + e.getTransactionId() + " committed]");
476
t.put(e.getTransactionId(), "");
477             }
478         }
479         return last;
480     }
481     
482     void reallyRestart() throws Exception JavaDoc {
483         //#ifdef DEBUG
484
if (Trace.bit(18)) {
485             Debug.println(toString() + ".reallyRestart()");
486         }
487         //#endif
488
db.getTempFile(false); // recover the scratch file.
489
Transaction t = db.makeTransaction(false);
490         LongMap map = new LongMap(32);
491         LogEntry last = scanLog(map);
492         int q = 0;
493         int checkpointPosition = logger.getCheckpoint();
494         int endPosition = logger.getEnd();
495         LogEntry op = last;
496         recovering = true;
497         try {
498             for (; op != null; op = logger.getPrevOp(op)) {
499                 LogEntry e = op;
500                 if (e != null && e.getCode() == LogEntry.STEP &&
501                     e.getPosition() < checkpointPosition &&
502                     map.get(e.getTransactionId()) == null) {
503                     if (e.getRedoState() == LogEntry.DONE) {
504                         //#ifdef DEBUG
505
if (Trace.bit(18)) {
506                             Debug.println("UNDO[" + e + "]");
507                         }
508                         //#endif
509
if (q++ == 0) {
510                             Debug.println("Undoing in-progress transactions...");
511                         }
512                         e.undo(t, db);
513                     }
514                 }
515             }
516             rowIdMap = null;
517
518             int p = 0;
519             for (op = logger.getFirstOp(); op != null; op = logger.getNextOp()) {
520                 LogEntry e = op;
521                 if (e.getPosition() >= endPosition) break;
522                 if (e != null && map.get(e.getTransactionId()) != null) {
523                     if (e.getCode() == LogEntry.STEP) {
524                         if (e.getRedoState() == LogEntry.DONE) {
525                             if (p++ == 0) {
526                                 Debug.println("Restoring committed transactions: " + map);
527                             }
528                             //#ifdef DEBUG
529
if (Trace.bit(18)) {
530                                 Debug.println("REDO[" + e + "]");
531                             }
532                             //#endif
533
e.redo(t, db);
534                         }
535                     }
536                 }
537             }
538             rowIdMap = null;
539         
540             if (p > 0 || q > 0) {
541                 Debug.println("Recovery complete: " +
542                               p + " redos, " +
543                               q + " undos");
544             }
545             logger.reset();
546             logger.sync();
547             if (p > 0 || q > 0) {
548                 checkpoint();
549             }
550         } finally {
551             recovering = false;
552         }
553     }
554
555     long lastCheckpoint = System.currentTimeMillis();
556     
557     void maybeCheckpoint() throws IOException JavaDoc {
558         long now = System.currentTimeMillis();
559         boolean idle = logger.getActiveTransactionCount() == 0;
560         if (idle) rowIdMap = null;
561         long interval = idle ? minSyncInterval : maxSyncInterval;
562         if (now - lastCheckpoint >= interval) {
563             //#ifdef DEBUG
564
if (Trace.bit(25)) {
565                 Debug.println("[" + entryCount + BlockStore.rw() +
566                               "] interval " + (now - lastCheckpoint) +
567                               " ms," + (idle ? " IDLE" : "") +
568                               " checkpoint now");
569             }
570             //#endif
571

572             reallyCheckpoint();
573
574             //#ifdef DEBUG
575
if (Trace.bit(25)) {
576                 Debug.println("checkpoint done");
577             }
578             //#endif
579
}
580     }
581
582     void reallyCheckpoint() throws IOException JavaDoc {
583         try {
584             db.flushRoot();
585             dbFile.flush(!checksync(4));
586             logger.checkpoint();
587
588             int numTrans = logger.getActiveTransactionCount();
589             boolean truncate = pendingBegins == 0 && numTrans == 0;
590             if (truncate) {
591                 logger.reset();
592             }
593             logger.sync();
594             db.checkpoint(truncate, !checksync(6));
595             db.checkpointHandler(logger.getActiveTransactions());
596             dbFile.clearModified();
597         } finally {
598             lastCheckpoint = System.currentTimeMillis();
599             rowIdMap = null;
600         }
601         //#ifdef DEBUG
602
if (Trace.bit(21)) {
603             Debug.println("AFTER Log1.reallyCheckpoint: [" +
604                           logger.getActiveTransactionCount() + "]");
605         }
606         if (false) {
607             long sum = 0;
608             long blk = dbFile.getBlockSize();
609             for (long x = 0; x < dbFile.getSize(); x += blk) {
610                 Block b = dbFile.getBlock(x / blk);
611                 for (int ix = 0; ix < blk; ix += 8) {
612                     sum += b.readLong(ix);
613                 }
614                 b.decrRefCount();
615             }
616             Debug.println("CHECKPOINT: CHECKSUM(" + dbFile.getSize() + ") bytes = " +
617                           sum);
618         }
619         //#endif
620
}
621
622     //#ifdef DEBUG
623
int entryCount = 0;
624     //#endif
625

626     public void reallyAddEntry(LogEntry entry) throws IOException JavaDoc {
627         //#ifdef DEBUG
628
entryCount++;
629         if (Trace.bit(16)) {
630             Debug.println(toString() + ".reallyAddEntry(" + entry + ")");
631         }
632         //#endif
633
try {
634             logger.put(entry);
635             if (entry.getCode() == LogEntry.BEGIN_TRANSACTION) {
636                 pendingBegins--;
637             }
638         } catch (IOException JavaDoc ex) {
639             if (ex.toString().indexOf("full") > 0) { // XXX need better way
640
abortOldestTransaction(entry);
641                 logger.put(entry);
642             } else {
643                 throw ex;
644             }
645         }
646     }
647
648     private void sortEntry(long tId, List JavaDoc save, List JavaDoc discard, LogEntry e) {
649         if (e.getTransactionId() == tId) {
650             discard.add(e);
651         } else {
652             save.add(e);
653         }
654     }
655
656     private final void abortOldestTransaction(LogEntry entry)
657         throws IOException JavaDoc
658     {
659         long tId = logger.getOldestTransaction();
660         Transaction t = db.findTransaction(tId);
661         if (t == null) {
662             Debug.println("Log full, will reset log");
663             logger.reset();
664             // XXX should this ever happen, we might
665
// XXX be better off if we aborted all the active transactions
666
} else {
667             ArrayList JavaDoc save = new ArrayList JavaDoc();
668             Debug.println("Log full, will abort " + t);
669             try {
670                 // First retrieve all of the pending log entries. There
671
// may be some for the doomed transaction -- we'll undo
672
// those first.
673
ArrayList JavaDoc discard = new ArrayList JavaDoc();
674                 LogEntry e;
675                 while ((e = (LogEntry)channel.poll(0)) != null) {
676                     sortEntry(tId, save, discard, e);
677                 }
678                 sortEntry(tId, save, discard, entry);
679                 Debug.println("discarding " + discard.size() +
680                               " completed log entries");
681                 for (int i = discard.size() - 1; i >= 0; i--) {
682                     e = (LogEntry)discard.get(i);
683                     try {
684                         e.undo(t, db);
685                     } catch (Throwable JavaDoc th) {
686                         //#ifdef DEBUG
687
Debug.println("Error during abort oldest");
688                         Debug.print(th);
689                         //#endif
690
}
691                 }
692                 reallyRollbackTransaction(t);
693             } catch (IOException JavaDoc ex1) {
694                 throw ex1;
695             } catch (Exception JavaDoc ex2) {
696                 //#ifdef DEBUG
697
Debug.print(ex2);
698                 //#endif
699
throw new DatafileException(ex2);
700             }
701             TransactionObserver obs = t.getObserver();
702             if (obs != null) {
703                 obs.abort(t);
704             }
705             reallyCheckpoint();
706             // After we've cleaned up the mess from the aborted transaction,
707
// we need to handle all of the queued log entries that we
708
// consumed.
709
Debug.println("handling " + save.size() + " saved log entries");
710             for (int i = 0; i < save.size(); i++) {
711                 LogEntry e = (LogEntry)save.get(i);
712                 try {
713                     e.handle(this);
714                 } catch (Throwable JavaDoc th) {
715                     Debug.print(th);
716                 }
717             }
718         }
719     }
720
721     /**
722      * XXXX Problem: If you have "the lock" and you call this when the channel
723      * is full, you may block. This would be bad, because it might block
724      * the log thread trying to get the lock, leading to deadlock
725      */

726     public void put(LogEntry h) throws IOException JavaDoc {
727         while (true) {
728             try {
729                 channel.put(h);
730             } catch (InterruptedException JavaDoc ex) {
731                 continue;
732             }
733             return;
734         }
735     }
736
737     /**
738      * Inner class to bind Flush op to 'log.reallyFlush()' method.
739      */

740     public static class Flush extends LogEntry {
741         public Flush() { super(FLUSH); }
742         public void handle(Log log) throws IOException JavaDoc {
743             ((Log1)log).reallyFlush();
744         }
745     }
746
747     /**
748      * A single static instance of this op is all we need.
749      */

750     static Flush opFlush = new Flush();
751
752     /**
753      * Inner class to bind Checkpoint op to 'log.reallyCheckpoint' method
754      */

755     public static class Checkpoint extends LogEntry {
756         public Checkpoint() { super(CHECKPOINT); }
757         public void handle(Log log) throws IOException JavaDoc {
758             ((Log1)log).reallyCheckpoint();
759         }
760     }
761     static Checkpoint opCheckpoint = new Checkpoint();
762
763     /**
764      * Inner class to bind Close op to 'log.reallyClose()' method
765      */

766     public static class Close extends LogEntry {
767         public Close() { super(CLOSE); }
768         public void handle(Log log) throws IOException JavaDoc {
769             ((Log1)log).reallyClose();
770         }
771     }
772
773     /**
774      * A single static instance of this op is all we need.
775      */

776     static Close opClose = new Close();
777
778     /**
779      * Inner class for transaction/statement rollback
780      */

781     public class Rollback extends LogEntry {
782         Transaction t;
783         public Rollback(Transaction t) {
784             super(t.getTransactionId(), ROLLBACK);
785             this.t = t;
786         }
787         public Rollback(Transaction t0, int s) {
788             super(t0.getTransactionId(), s, ROLLBACK);
789             this.t = t0;
790         }
791         public void handle(Log log) throws Exception JavaDoc {
792             if (stmtId == -1) {
793                 reallyRollbackTransaction(t);
794             } else {
795                 reallyRollbackStatement(t, stmtId);
796             }
797         }
798     }
799     
800     /**
801      * Inner class for log thread syncing
802      */

803     public class Sync extends LogEntry {
804         Latch latch;
805         public Sync(Latch latch) {
806             super(SYNC);
807             this.latch = latch;
808         }
809         public void handle(Log log) throws Exception JavaDoc {
810             latch.release();
811         }
812     }
813     
814     /**
815      * Private inner class which implements single-threaded log file
816      * writer, using a Channel.
817      */

818     class LogSync extends Thread JavaDoc {
819         boolean closeMe = false;
820         Log1 log = null;
821         LogSync(Log1 log) { super("Log Sync"); this.log = log; }
822
823         public void close() {
824             closeMe = true;
825         }
826         
827         public void run() {
828             try {
829                 while (!closeMe) {
830                     Object JavaDoc obj = channel.poll(500);
831                     if (obj != null) {
832                         // once we've got something to do, keep the lock
833
// until we're finished.
834
synchronized (fileLock) {
835                             while (obj != null) {
836                                 try {
837                                     //#ifdef DEBUG
838
if (Trace.bit(17)) {
839                                         Debug.println("PRE [" + obj + "].handle()");
840                                     }
841                                     //#endif
842
((LogEntry)obj).handle(log);
843                                     //#ifdef DEBUG
844
if (Trace.bit(15)) {
845                                         Debug.println("POST [" + obj + "].handle()");
846                                     }
847                                     //#endif
848
} catch (IOException JavaDoc ex) {
849                                     Debug.println("LogSync: Got exception in [" + obj + "].handle()");
850                                     Debug.print(ex);
851                                 } catch (Throwable JavaDoc t) {
852                                     Debug.println("LogSync: Got exception in [" + obj + "].handle()");
853                                     Debug.print(t);
854                                 }
855                                 obj = channel.poll(0);
856                             }
857                             if (!closeMe) {
858                                 log.maybeCheckpoint();
859                             }
860                         }
861                     }
862                 }
863                 log = null;
864                 if (logger != null) logger.close();
865             } catch (InterruptedException JavaDoc ex) {
866                 Debug.print(ex);
867             } catch (Throwable JavaDoc t) {
868                 Debug.print(t);
869             } finally {
870                 //#ifdef DEBUG
871
//Debug.println("Log Sync thread exiting **** ");
872
//#endif
873
closeLatch.release();
874             }
875         }
876
877     }
878
879     /**
880      * Are you logging?
881      *
882      * Oh yes.
883      */

884     public boolean isLogging() { return true; }
885
886     /**
887      * Are we currently performing recovery?
888      */

889     public boolean inRecovery() {
890         return recovering;
891     }
892
893     //#ifdef DEBUG
894
int filepos = 0;
895     //#endif
896

897     /**
898      * Save a "before" image
899      */

900     byte[] sav = null;
901     public void saveBlock(long b) throws IOException JavaDoc {
902         if (sav == null) {
903             sav = new byte[blockSize + 8];
904         }
905         ByteUtil.putLong(sav, 0, b);
906         dbFile.store.read(b, sav, 8);
907         if (bfo == null) resetBlocks();
908         bfo.write(sav, 0, sav.length);
909         bfoActive = true;
910         //#ifdef DEBUG
911
if (Trace.bit(19)) Debug.println("saveBlock(" + b + ", " + Block.signature(sav, 8, blockSize) + " @ " + filepos + ")");
912         filepos += sav.length;
913         //#endif
914
if (checksync(7)) {
915             try {
916                 bfo.getFD().sync();
917             } catch (Throwable JavaDoc t) {
918             } finally {
919                 bfoActive = false;
920             }
921         }
922     }
923
924     /**
925      * Restore all the "before" images
926      */

927     public void restoreBlocks() throws IOException JavaDoc {
928         //#ifdef DEBUG
929
if (Trace.bit(19)) {
930             Debug.println(db.toString() +
931                           ".restoreBlocks(): size = " + dbFile.getSize());
932         }
933         //#endif
934
File JavaDoc f = new File JavaDoc(db.getScratchDir(), "before-images");
935         int siz = blockSize + 8;
936         if (f.exists() && f.length() >= siz) {
937             RandomAccessFile JavaDoc bf = new RandomAccessFile JavaDoc(f, "r");
938             try {
939                 byte[] buf = new byte[siz];
940                 long pos = bf.length() - siz;
941                 while (pos >= 0) {
942                     bf.seek(pos);
943                     bf.read(buf);
944                     long blk = ByteUtil.getLong(buf, 0);
945                     //#ifdef DEBUG
946
if (Trace.bit(19)) {
947                         Debug.println(db.toString() + " [RESTORE " + blk + " @ " + pos + "]");
948                     }
949                     //#endif
950
dbFile.restoreBlock(blk, buf, 8);
951                     pos -= siz;
952                 }
953             } finally {
954                 bf.close();
955             }
956         }
957         //#ifdef DEBUG
958
if (Trace.bit(19)) {
959             Debug.println(db.toString() +
960                           ".restoreBlocks(): size = " + dbFile.getSize());
961         }
962         if (false) {
963             long sum = 0;
964             long blk = dbFile.getBlockSize();
965             for (long x = 0; x < dbFile.getSize(); x += blk) {
966                 Block b = dbFile.getBlock(x / blk);
967                 for (int ix = 0; ix < blk; ix += 8) {
968                     sum += b.readLong(ix);
969                 }
970                 b.decrRefCount();
971             }
972             Debug.println("RECOVER: CHECKSUM(" + dbFile.getSize() + ") bytes = " +
973                           sum);
974         }
975         //#endif
976
}
977
978     /**
979      * Reset the "before" list to be empty
980      */

981     public void resetBlocks() throws IOException JavaDoc {
982         if (bfo != null) {
983             try {
984                 bfo.close();
985             } catch (Throwable JavaDoc t) {
986             } finally {
987                 bfo = null;
988             }
989         }
990         File JavaDoc b = new File JavaDoc(db.getScratchDir(), "before-images");
991         bfo = new FileOutputStream JavaDoc(b);
992         bfoActive = false;
993         //#ifdef DEBUG
994
if (Trace.bit(19)) Debug.println(db.toString() + ".Log1.resetBlocks() @ " + filepos + ", dbFile.size = " + dbFile.getSize());
995         filepos = 0;
996         //#endif
997
}
998
999     //#ifdef DEBUG
1000
public String JavaDoc toString() {
1001        String JavaDoc s = getClass().getName();
1002        int x = s.lastIndexOf('.');
1003        if (x >= 0) s = s.substring(x+1);
1004        return s;
1005    }
1006    //#endif
1007
}
1008
1009
Popular Tags