KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > store > LogFile


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.store;
6
7 import java.io.IOException JavaDoc;
8 import java.sql.SQLException JavaDoc;
9
10 import org.h2.api.DatabaseEventListener;
11 import org.h2.engine.Constants;
12 import org.h2.engine.Database;
13 import org.h2.engine.Session;
14 import org.h2.message.Message;
15 import org.h2.message.Trace;
16 import org.h2.util.FileUtils;
17 import org.h2.util.MathUtils;
18 import org.h2.util.ObjectArray;
19
20 /*
21  * Header format:
22  * intfixed logId (<0 means ignore: rolled back already)
23  * intfixed firstUncommittedLogRecordId (-1 if none)
24  * intfixed firstUnwrittenLogRecordId (-1 if none)
25  *
26  * Record format:
27  * int block size
28  * byte 'D' (delete) / 'I' (insert) / 'C' (commit) / 'R' (rollback) / 'P' (prepare commit) / 'T' (truncate)
29  * int session
30  * [delete/insert only:]
31  * int storage
32  * int record.pos
33  * int record.blockcount
34  * [prepare commit only:]
35  * string transaction
36  */

37 public class LogFile {
38
39     private static final int BUFFER_SIZE = 8 * 1024;
40     public static final int BLOCK_SIZE = 16;
41
42     private LogSystem logSystem;
43     private Database database;
44     private int id;
45     private String JavaDoc fileNamePrefix;
46     private String JavaDoc fileName;
47     private FileStore file;
48     private int bufferPos;
49     private byte[] buffer;
50     private ObjectArray unwritten;
51     private DataPage rowBuff;
52     private int pos = LogSystem.LOG_WRITTEN;
53     private int firstUncommittedPos = LogSystem.LOG_WRITTEN;
54     private int firstUnwrittenPos = LogSystem.LOG_WRITTEN;
55
56     LogFile(LogSystem log, int id, String JavaDoc fileNamePrefix) throws SQLException JavaDoc {
57         this.logSystem = log;
58         this.database = log.getDatabase();
59         this.id = id;
60         this.fileNamePrefix = fileNamePrefix;
61         fileName = getFileName(id);
62         file = log.getDatabase().openFile(fileName, false);
63         rowBuff = log.getRowBuffer();
64         buffer = new byte[BUFFER_SIZE];
65         unwritten = new ObjectArray();
66         try {
67             readHeader();
68             writeHeader();
69             pos = getBlock();
70             firstUncommittedPos = pos;
71         } catch(SQLException JavaDoc e) {
72             close(false);
73             throw e;
74         }
75     }
76     
77     static LogFile openIfLogFile(LogSystem log, String JavaDoc fileNamePrefix, String JavaDoc fileName) throws SQLException JavaDoc {
78         if(!fileName.endsWith(Constants.SUFFIX_LOG_FILE)) {
79             return null;
80         }
81         if(!FileUtils.fileStartsWith(fileName, fileNamePrefix+".")) {
82             return null;
83         }
84         String JavaDoc s = fileName.substring(fileNamePrefix.length()+1, fileName.length()-Constants.SUFFIX_LOG_FILE.length());
85         int id = Integer.parseInt(s);
86         return new LogFile(log, id, fileNamePrefix);
87     }
88     
89     private String JavaDoc getFileName(int id) {
90         return fileNamePrefix + "." + id + Constants.SUFFIX_LOG_FILE;
91     }
92
93     public int getId() {
94         return id;
95     }
96     
97     private int getBlock() throws SQLException JavaDoc {
98         if(file == null) {
99             throw Message.getSQLException(Message.SIMULATED_POWER_OFF);
100         }
101         return (int)(file.getFilePointer() / BLOCK_SIZE);
102     }
103
104     private void writeBuffer(DataPage buff, Record rec) throws SQLException JavaDoc {
105         if(file == null) {
106             throw Message.getSQLException(Message.SIMULATED_POWER_OFF);
107         }
108         int size = MathUtils.roundUp(buff.length()+buff.getFillerLength(), BLOCK_SIZE);
109         int blockCount = size / BLOCK_SIZE;
110         buff.fill(size);
111         buff.setInt(0, blockCount);
112         buff.updateChecksum();
113 // IOLogger.getInstance().logWrite(this.fileName, file.getFilePointer(), buff.length());
114
if (buff.length() + bufferPos > buffer.length) {
115             // the buffer is full
116
flush();
117         }
118         if (buff.length() >= buffer.length) {
119             // special case really long write request: write it without buffering
120
file.write(buff.getBytes(), 0, buff.length());
121             pos = getBlock();
122             return;
123         }
124         System.arraycopy(buff.getBytes(), 0, buffer, bufferPos, buff.length());
125         bufferPos += buff.length();
126         if(rec != null) {
127             unwritten.add(rec);
128         }
129         pos = getBlock() + (bufferPos / BLOCK_SIZE);
130     }
131     
132     void commit(Session session) throws SQLException JavaDoc {
133         DataPage buff = rowBuff;
134         buff.reset();
135         buff.writeInt(0);
136         buff.writeByte((byte)'C');
137         buff.writeInt(session.getId());
138         writeBuffer(buff, null);
139         if(logSystem.getFlushOnEachCommit()) {
140             flush();
141         }
142     }
143     
144     void prepareCommit(Session session, String JavaDoc transaction) throws SQLException JavaDoc {
145         DataPage buff = rowBuff;
146         buff.reset();
147         buff.writeInt(0);
148         buff.writeByte((byte)'P');
149         buff.writeInt(session.getId());
150         buff.writeString(transaction);
151         writeBuffer(buff, null);
152         if(logSystem.getFlushOnEachCommit()) {
153             flush();
154         }
155     }
156
157     private DataPage readPage() throws SQLException JavaDoc {
158         byte[] buff = new byte[BLOCK_SIZE];
159         file.readFully(buff, 0, BLOCK_SIZE);
160         DataPage s = DataPage.create(database, buff);
161         int blocks = Math.abs(s.readInt());
162         if(blocks > 1) {
163             byte[] b2 = new byte[blocks * BLOCK_SIZE];
164             System.arraycopy(buff, 0, b2, 0, BLOCK_SIZE);
165             buff = b2;
166             file.readFully(buff, BLOCK_SIZE, blocks * BLOCK_SIZE - BLOCK_SIZE);
167             s = DataPage.create(database, buff);
168             s.check(blocks * BLOCK_SIZE);
169         } else {
170             s.reset();
171         }
172         return s;
173     }
174
175     private boolean redoOrUndo(boolean undo) throws SQLException JavaDoc {
176         int pos = getBlock();
177         DataPage in = readPage();
178         int blocks = in.readInt();
179         if(blocks < 0) {
180             return true;
181         } else if(blocks == 0) {
182             go(pos);
183             file.setLength((long)pos * BLOCK_SIZE);
184             return false;
185         }
186         char type = (char)in.readByte();
187         int sessionId = in.readInt();
188         if(type == 'P') {
189             if(undo) {
190                 throw Message.getInternalError("can't undo prepare commit");
191             }
192             String JavaDoc transaction = in.readString();
193             logSystem.setPreparedCommitForSession(this, sessionId, pos, transaction, blocks);
194             return true;
195         } else if(type == 'C') {
196             if(undo) {
197                 throw Message.getInternalError("can't undo commit");
198             }
199             logSystem.setLastCommitForSession(sessionId, id, pos);
200             return true;
201         } else if(type == 'R') {
202             if(undo) {
203                 throw Message.getInternalError("can't undo rollback");
204             }
205             return true;
206         } else if(type == 'S') {
207             if(undo) {
208                 throw Message.getInternalError("can't undo summary");
209             }
210         }
211         if(undo) {
212             if(logSystem.isSessionCommitted(sessionId, id, pos)) {
213                 logSystem.removeSession(sessionId);
214                 return true;
215             }
216         } else {
217             if(type != 'S') {
218                 logSystem.addUndoLogRecord(this, pos, sessionId);
219             }
220         }
221         int storageId = in.readInt();
222         Storage storage = logSystem.getStorageForRecovery(storageId);
223         DataPage rec = null;
224         int recordId = in.readInt();
225         int blockCount = in.readInt();
226         if(type != 'T') {
227             rec = in.readDataPageNoSize();
228         }
229         switch(type) {
230         case 'S': {
231             int fileType = in.readByte();
232             boolean diskFile;
233             if(fileType == 'D') {
234                 diskFile = true;
235             } else if(fileType == 'I') {
236                 diskFile = false;
237             } else {
238                 // unknown type, maybe linear index file (future)
239
break;
240             }
241             int sumLength= in.readInt();
242             byte[] summary = new byte[sumLength];
243             if(sumLength > 0) {
244                 in.read(summary, 0, sumLength);
245             }
246             if(diskFile) {
247                 database.getDataFile().initFromSummary(summary);
248             } else {
249                 database.getIndexFile().initFromSummary(summary);
250             }
251             break;
252         }
253         case 'T':
254             if(undo) {
255                 throw Message.getInternalError("cannot undo truncate");
256             } else {
257                 logSystem.addRedoLog(storage, recordId, blockCount, null);
258                 storage.setRecordCount(0);
259                 storage.getDiskFile().setPageOwner(recordId / DiskFile.BLOCKS_PER_PAGE, -1);
260                 logSystem.setLastCommitForSession(sessionId, id, pos);
261             }
262             break;
263         case 'I':
264             if(undo) {
265                 logSystem.addRedoLog(storage, recordId, blockCount, null);
266                 storage.setRecordCount(storage.getRecordCount()-1);
267             } else {
268                 logSystem.addRedoLog(storage, recordId, blockCount, rec);
269                 storage.setRecordCount(storage.getRecordCount()+1);
270             }
271             break;
272         case 'D':
273             if(undo) {
274                 logSystem.addRedoLog(storage, recordId, blockCount, rec);
275                 storage.setRecordCount(storage.getRecordCount()+1);
276             } else {
277                 logSystem.addRedoLog(storage, recordId, blockCount, null);
278                 storage.setRecordCount(storage.getRecordCount()-1);
279             }
280             break;
281         default:
282             throw Message.getInternalError("type="+type);
283         }
284         return true;
285     }
286     
287     public void redoAllGoEnd() throws SQLException JavaDoc {
288         long length = file.length();
289         if(length<=FileStore.HEADER_LENGTH) {
290             return;
291         }
292         try {
293             int max = (int)(length / BLOCK_SIZE);
294             while(true) {
295                 pos = getBlock();
296                 database.setProgress(DatabaseEventListener.STATE_RECOVER, fileName, pos, max);
297                 if((long)pos * BLOCK_SIZE >= length) {
298                     break;
299                 }
300                 boolean more = redoOrUndo(false);
301                 if(!more) {
302                     break;
303                 }
304             }
305             database.setProgress(DatabaseEventListener.STATE_RECOVER, fileName, max, max);
306         } catch(SQLException JavaDoc e) {
307             database.getTrace(Trace.LOG).debug("Stop reading log file: "+e.getMessage(), e);
308             // wrong checksum is ok (at the end of the log file)
309
} catch(OutOfMemoryError JavaDoc e) {
310             // OutOfMemoryError means not enough memory is allocated to the VM.
311
// this is not necessarily at the end of the log file
312
throw Message.convert(e);
313         } catch(Throwable JavaDoc e) {
314             database.getTrace(Trace.LOG).error("Error reading log file (non-fatal)", e);
315             // TODO log exception, but mark as 'probably ok'
316
// on power loss, sometime there is garbage at the end of the file
317
// we stop recovering in this case (checksum mismatch)
318
}
319         go(pos);
320     }
321     
322     void go(int pos) throws SQLException JavaDoc {
323         file.seek((long)pos * BLOCK_SIZE);
324     }
325     
326     void undo(int pos) throws SQLException JavaDoc {
327         go(pos);
328         redoOrUndo(true);
329     }
330
331     void flush() throws SQLException JavaDoc {
332         if(bufferPos > 0) {
333             if(file == null) {
334                 throw Message.getSQLException(Message.SIMULATED_POWER_OFF);
335             }
336             file.write(buffer, 0, bufferPos);
337             for(int i=0; i<unwritten.size(); i++) {
338                 Record r = (Record) unwritten.get(i);
339                 r.setLogWritten(id, pos);
340             }
341             unwritten.clear();
342             bufferPos = 0;
343             pos = getBlock();
344             long min = (long)pos * BLOCK_SIZE;
345             min = MathUtils.scaleUp50Percent(128 * 1024, min, 8 * 1024);
346             if(min > file.length()) {
347                 file.setLength(min);
348             }
349         }
350     }
351     
352     void close(boolean delete) throws SQLException JavaDoc {
353         SQLException JavaDoc closeException = null;
354         try {
355             flush();
356         } catch (SQLException JavaDoc e) {
357             closeException = e;
358         }
359         // continue with close even if flush was not possible (file storage problem)
360
if(file != null) {
361             try {
362                 file.close();
363                 file = null;
364                 if(delete) {
365                     FileUtils.delete(fileName);
366                 }
367             } catch (IOException JavaDoc e) {
368                 if(closeException == null) {
369                     closeException = Message.convert(e);
370                 }
371             }
372             file = null;
373             fileNamePrefix = null;
374         }
375         if(closeException != null) {
376             throw closeException;
377         }
378     }
379     
380     void addSummary(boolean dataFile, byte[] summary) throws SQLException JavaDoc {
381         DataPage buff = DataPage.create(database, 256);
382         buff.writeInt(0);
383         buff.writeByte((byte)'S');
384         buff.writeInt(0);
385         buff.writeInt(0); // storageId
386
buff.writeInt(0); // recordId
387
buff.writeInt(0); // blockCount
388
buff.writeByte((byte)(dataFile ? 'D' : 'I'));
389         if(summary == null) {
390             buff.writeInt(0);
391         } else {
392             buff.checkCapacity(summary.length);
393             buff.writeInt(summary.length);
394             buff.write(summary, 0, summary.length);
395         }
396         writeBuffer(buff, null);
397     }
398
399     void addTruncate(Session session, int storageId, int recordId, int blockCount) throws SQLException JavaDoc {
400         DataPage buff = rowBuff;
401         buff.reset();
402         buff.writeInt(0);
403         buff.writeByte((byte)'T');
404         buff.writeInt(session.getId());
405         buff.writeInt(storageId);
406         buff.writeInt(recordId);
407         buff.writeInt(blockCount);
408         writeBuffer(buff, null);
409     }
410
411     void add(Session session, int storageId, Record record) throws SQLException JavaDoc {
412         record.prepareWrite();
413         DataPage buff = rowBuff;
414         buff.reset();
415         buff.writeInt(0);
416         if(record.getDeleted()) {
417             buff.writeByte((byte)'D');
418         } else {
419             buff.writeByte((byte)'I');
420         }
421         buff.writeInt(session.getId());
422         buff.writeInt(storageId);
423         buff.writeInt(record.getPos());
424         int blockCount = record.getBlockCount();
425         buff.writeInt(blockCount);
426         buff.checkCapacity(DiskFile.BLOCK_SIZE * blockCount);
427         record.write(buff);
428         writeBuffer(buff, record);
429     }
430     
431     void setFirstUncommittedPos(int firstUncommittedPos) throws SQLException JavaDoc {
432         this.firstUncommittedPos = firstUncommittedPos;
433         int pos = getBlock();
434         writeHeader();
435         go(pos);
436     }
437     
438     int getFirstUncommittedPos() {
439         return firstUncommittedPos;
440     }
441     
442     private void writeHeader() throws SQLException JavaDoc {
443         file.seek(FileStore.HEADER_LENGTH);
444         DataPage buff = getHeader();
445         file.write(buff.getBytes(), 0, buff.length());
446     }
447     
448     private DataPage getHeader() {
449         DataPage buff = rowBuff;
450         buff.reset();
451         buff.writeInt(id);
452         buff.writeInt(firstUncommittedPos);
453         // TODO need to update & use firstUnwrittenPos
454
buff.writeInt(firstUnwrittenPos);
455         buff.fill(3*BLOCK_SIZE);
456         return buff;
457     }
458     
459     private void readHeader() throws SQLException JavaDoc {
460         DataPage buff = getHeader();
461         int len = buff.length();
462         buff.reset();
463         if(file.length() < FileStore.HEADER_LENGTH + len) {
464             // this is an empty file
465
return;
466         }
467         file.readFully(buff.getBytes(), 0, len);
468         id = buff.readInt();
469         firstUncommittedPos = buff.readInt();
470         firstUnwrittenPos = buff.readInt();
471     }
472
473     int getPos() {
474         return pos;
475     }
476
477     public long getFileSize() throws SQLException JavaDoc {
478         return file.getFilePointer();
479     }
480
481     public void sync() {
482         if(file != null) {
483             file.sync();
484         }
485     }
486
487     public void updatePreparedCommit(boolean commit, int pos, int sessionId, int blocks) throws SQLException JavaDoc {
488         synchronized(database) {
489             int posNow = getBlock();
490             DataPage buff = rowBuff;
491             buff.reset();
492             buff.writeInt(blocks);
493             if(commit) {
494                 buff.writeByte((byte)'C');
495             } else {
496                 buff.writeByte((byte)'R');
497             }
498             buff.writeInt(sessionId);
499             buff.fill(blocks * BLOCK_SIZE);
500             buff.updateChecksum();
501             go(pos);
502             file.write(buff.getBytes(), 0, BLOCK_SIZE * blocks);
503             go(posNow);
504         }
505     }
506
507 }
508
Popular Tags