KickJava   Java API By Example, From Geeks To Geeks.

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


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.sql.SQLException JavaDoc;
8 import java.util.Comparator JavaDoc;
9 import java.util.HashMap JavaDoc;
10
11 import org.h2.api.DatabaseEventListener;
12 import org.h2.engine.Constants;
13 import org.h2.engine.Database;
14 import org.h2.engine.Session;
15 import org.h2.util.FileUtils;
16 import org.h2.util.ObjectArray;
17
18 /**
19  * @author Thomas
20  */

21 public class LogSystem {
22
23     public static final int LOG_WRITTEN = -1;
24
25     private Database database;
26     private ObjectArray activeLogs;
27     private LogFile currentLog;
28     private String JavaDoc fileNamePrefix;
29     private HashMap JavaDoc storages = new HashMap JavaDoc();
30     private HashMap JavaDoc sessions = new HashMap JavaDoc();
31     private DataPage rowBuff;
32     private ObjectArray undo;
33     // TODO log file / deleteOldLogFilesAutomatically: make this a setting, so they can be backed up
34
private boolean deleteOldLogFilesAutomatically = true;
35     private long maxLogSize = Constants.DEFAULT_MAX_LOG_SIZE;
36     private boolean readOnly;
37     private boolean flushOnEachCommit;
38     private ObjectArray inDoubtTransactions;
39     private boolean disabled;
40
41     public LogSystem(Database database, String JavaDoc fileNamePrefix, boolean readOnly) throws SQLException JavaDoc {
42         this.database = database;
43         this.readOnly = readOnly;
44         if (database == null || readOnly) {
45             return;
46         }
47         this.fileNamePrefix = fileNamePrefix;
48         rowBuff = DataPage.create(database, Constants.DEFAULT_DATA_PAGE_SIZE);
49         loadActiveLogFiles();
50     }
51
52     public void setMaxLogSize(long maxSize) {
53         this.maxLogSize = maxSize;
54     }
55
56     public long getMaxLogSize() {
57         return maxLogSize;
58     }
59
60     public boolean containsInDoubtTransactions() {
61         return inDoubtTransactions != null && inDoubtTransactions.size() > 0;
62     }
63
64     private void flushAndCloseUnused() throws SQLException JavaDoc {
65         currentLog.flush();
66         DiskFile file = database.getDataFile();
67         if (file == null) {
68             return;
69         }
70         file.flush();
71         if (containsInDoubtTransactions()) {
72             // if there are any in-doubt transactions (even if they are resolved), can't update or delete the log files
73
return;
74         }
75         Session[] sessions = database.getSessions();
76         int firstUncommittedLog = currentLog.getId();
77         int firstUncommittedPos = currentLog.getPos();
78         for (int i = 0; i < sessions.length; i++) {
79             Session session = sessions[i];
80             int log = session.getFirstUncommittedLog();
81             int pos = session.getFirstUncommittedPos();
82             if (pos != LOG_WRITTEN) {
83                 if (log < firstUncommittedLog || (log == firstUncommittedLog && pos < firstUncommittedPos)) {
84                     firstUncommittedLog = log;
85                     firstUncommittedPos = pos;
86                 }
87             }
88         }
89         for (int i = activeLogs.size() - 1; i >= 0; i--) {
90             LogFile l = (LogFile) activeLogs.get(i);
91             if (l.getId() < firstUncommittedLog) {
92                 l.setFirstUncommittedPos(LOG_WRITTEN);
93             } else if (l.getId() == firstUncommittedLog) {
94                 if (firstUncommittedPos == l.getPos()) {
95                     l.setFirstUncommittedPos(LOG_WRITTEN);
96                 } else {
97                     l.setFirstUncommittedPos(firstUncommittedPos);
98                 }
99             }
100         }
101         for (int i = 0; i < activeLogs.size(); i++) {
102             LogFile l = (LogFile) activeLogs.get(i);
103             if (l.getFirstUncommittedPos() == LOG_WRITTEN) {
104                 l.close(deleteOldLogFilesAutomatically);
105                 activeLogs.remove(i);
106                 i--;
107             }
108         }
109     }
110
111     public void close() throws SQLException JavaDoc {
112         if (database == null || readOnly) {
113             return;
114         }
115         synchronized (database) {
116             // TODO refactor flushing and closing files when we know what to do exactly
117
SQLException JavaDoc closeException = null;
118             try {
119                 flushAndCloseUnused();
120                 if (!containsInDoubtTransactions()) {
121                     checkpoint();
122                 }
123             } catch (SQLException JavaDoc e) {
124                 closeException = e;
125             }
126             for (int i = 0; i < activeLogs.size(); i++) {
127                 LogFile l = (LogFile) activeLogs.get(i);
128                 try {
129                     // if there are any in-doubt transactions (even if they are resolved), can't delete the log files
130
if (l.getFirstUncommittedPos() == LOG_WRITTEN && !containsInDoubtTransactions()) {
131                         l.close(deleteOldLogFilesAutomatically);
132                     } else {
133                         l.close(false);
134                     }
135                 } catch (SQLException JavaDoc e) {
136                     // TODO log exception
137
if (closeException == null) {
138                         closeException = e;
139                     }
140                 }
141             }
142             database = null;
143             if (closeException != null) {
144                 throw closeException;
145             }
146         }
147     }
148
149     boolean needMoreUndo() {
150         return sessions.size() > 0;
151     }
152
153     void addUndoLogRecord(LogFile log, int logRecordId, int sessionId) {
154         LogRecord record = new LogRecord(log, logRecordId, sessionId);
155         undo.add(record);
156     }
157
158     public boolean recover() throws SQLException JavaDoc {
159         if (database == null) {
160             return false;
161         }
162         synchronized (database) {
163             undo = new ObjectArray();
164             for (int i = 0; i < activeLogs.size(); i++) {
165                 LogFile log = (LogFile) activeLogs.get(i);
166                 log.redoAllGoEnd();
167             }
168             database.getDataFile().flushRedoLog();
169             database.getIndexFile().flushRedoLog();
170             int end = currentLog.getPos();
171             Object JavaDoc[] states = sessions.values().toArray();
172             inDoubtTransactions = new ObjectArray();
173             for (int i = 0; i < states.length; i++) {
174                 SessionState state = (SessionState) states[i];
175                 if (state.inDoubtTransaction != null) {
176                     inDoubtTransactions.add(state.inDoubtTransaction);
177                 }
178             }
179             for (int i = undo.size() - 1; i >= 0 && sessions.size() > 0; i--) {
180                 database.setProgress(DatabaseEventListener.STATE_RECOVER, null, undo.size() - 1 - i, undo.size());
181                 LogRecord record = (LogRecord) undo.get(i);
182                 if (sessions.get(new Integer JavaDoc(record.sessionId)) != null) {
183                     // undo only if the session is not yet committed
184
record.log.undo(record.logRecordId);
185                     database.getDataFile().flushRedoLog();
186                     database.getIndexFile().flushRedoLog();
187                 }
188             }
189             currentLog.go(end);
190             boolean fileChanged = undo.size() > 0;
191             undo = null;
192             storages.clear();
193             if (fileChanged && !containsInDoubtTransactions()) {
194                 checkpoint();
195             }
196             return fileChanged;
197         }
198     }
199
200     private void loadActiveLogFiles() throws SQLException JavaDoc {
201         String JavaDoc path = FileUtils.getParent(fileNamePrefix);
202         String JavaDoc[] list = FileUtils.listFiles(path);
203         activeLogs = new ObjectArray();
204         for (int i = 0; i < list.length; i++) {
205             String JavaDoc s = list[i];
206             LogFile l = LogFile.openIfLogFile(this, fileNamePrefix, s);
207             if (l != null) {
208                 if (l.getPos() == LOG_WRITTEN) {
209                     l.close(deleteOldLogFilesAutomatically);
210                 } else {
211                     activeLogs.add(l);
212                 }
213             }
214         }
215         activeLogs.sort(new Comparator JavaDoc() {
216             public int compare(Object JavaDoc a, Object JavaDoc b) {
217                 return ((LogFile) a).getId() - ((LogFile) b).getId();
218             }
219         });
220         if (activeLogs.size() == 0) {
221             LogFile l = new LogFile(this, 0, fileNamePrefix);
222             activeLogs.add(l);
223         }
224         currentLog = (LogFile) activeLogs.get(activeLogs.size() - 1);
225     }
226
227     Storage getStorageForRecovery(int id) throws SQLException JavaDoc {
228         boolean dataFile;
229         if (id < 0) {
230             dataFile = false;
231             id = -id;
232         } else {
233             dataFile = true;
234         }
235         Integer JavaDoc i = new Integer JavaDoc(id);
236         Storage storage = (Storage) storages.get(i);
237         if (storage == null) {
238             storage = database.getStorage(null, id, dataFile);
239             storages.put(i, storage);
240         }
241         return storage;
242     }
243
244     boolean isSessionCommitted(int sessionId, int logId, int pos) {
245         Integer JavaDoc key = new Integer JavaDoc(sessionId);
246         SessionState state = (SessionState) sessions.get(key);
247         if (state == null) {
248             return true;
249         }
250         return state.isCommitted(logId, pos);
251     }
252
253     void setLastCommitForSession(int sessionId, int logId, int pos) {
254         Integer JavaDoc key = new Integer JavaDoc(sessionId);
255         SessionState state = (SessionState) sessions.get(key);
256         if (state == null) {
257             state = new SessionState();
258             sessions.put(key, state);
259             state.sessionId = sessionId;
260         }
261         state.lastCommitLog = logId;
262         state.lastCommitPos = pos;
263         state.inDoubtTransaction = null;
264     }
265
266     void setPreparedCommitForSession(LogFile log, int sessionId, int pos, String JavaDoc transaction, int blocks) {
267         Integer JavaDoc key = new Integer JavaDoc(sessionId);
268         SessionState state = (SessionState) sessions.get(key);
269         if (state == null) {
270             state = new SessionState();
271             sessions.put(key, state);
272             state.sessionId = sessionId;
273         }
274         // this is potentially a commit, so don't roll back the action before it (currently)
275
setLastCommitForSession(sessionId, log.getId(), pos);
276         state.inDoubtTransaction = new InDoubtTransaction(log, sessionId, pos, transaction, blocks);
277     }
278
279     public ObjectArray getInDoubtTransactions() {
280         return inDoubtTransactions;
281     }
282
283     void removeSession(int sessionId) {
284         sessions.remove(new Integer JavaDoc(sessionId));
285     }
286
287     public void prepareCommit(Session session, String JavaDoc transaction) throws SQLException JavaDoc {
288         if (database == null || readOnly) {
289             return;
290         }
291         synchronized (database) {
292             currentLog.prepareCommit(session, transaction);
293         }
294     }
295
296     public void commit(Session session) throws SQLException JavaDoc {
297         if (database == null || readOnly) {
298             return;
299         }
300         synchronized (database) {
301             currentLog.commit(session);
302             session.setAllCommitted();
303         }
304     }
305
306     public void flush() throws SQLException JavaDoc {
307         if (database == null || readOnly) {
308             return;
309         }
310         synchronized (database) {
311             currentLog.flush();
312         }
313     }
314
315     public void addTruncate(Session session, DiskFile file, int storageId, int recordId, int blockCount) throws SQLException JavaDoc {
316         if (database == null || disabled) {
317             return;
318         }
319         synchronized (database) {
320             database.checkWritingAllowed();
321             if (!file.isDataFile()) {
322                 storageId = -storageId;
323             }
324             currentLog.addTruncate(session, storageId, recordId, blockCount);
325             if (currentLog.getFileSize() > maxLogSize) {
326                 checkpoint();
327             }
328         }
329     }
330
331     public void add(Session session, DiskFile file, Record record) throws SQLException JavaDoc {
332         if (database == null || disabled) {
333             return;
334         }
335         synchronized (database) {
336             database.checkWritingAllowed();
337             int storageId = record.getStorageId();
338             if (!file.isDataFile()) {
339                 storageId = -storageId;
340             }
341             int log = currentLog.getId();
342             int pos = currentLog.getPos();
343             session.addLogPos(log, pos);
344             record.setLastLog(log, pos);
345             currentLog.add(session, storageId, record);
346             if (currentLog.getFileSize() > maxLogSize) {
347                 checkpoint();
348             }
349         }
350     }
351
352     public void checkpoint() throws SQLException JavaDoc {
353         if (database == null || readOnly || disabled) {
354             return;
355         }
356         synchronized (database) {
357             flushAndCloseUnused();
358             currentLog = new LogFile(this, currentLog.getId() + 1, fileNamePrefix);
359             activeLogs.add(currentLog);
360             writeSummary();
361             currentLog.flush();
362         }
363     }
364
365     private void writeSummary() throws SQLException JavaDoc {
366         if (database == null || readOnly || disabled) {
367             return;
368         }
369         byte[] summary;
370         DiskFile file;
371         file = database.getDataFile();
372         summary = file.getSummary();
373         if (summary != null) {
374             currentLog.addSummary(true, summary);
375         }
376         if (database.getLogIndexChanges() || database.getIndexSummaryValid()) {
377             file = database.getIndexFile();
378             summary = file.getSummary();
379             if (summary != null) {
380                 currentLog.addSummary(false, summary);
381             }
382         } else {
383             // invalidate the index summary
384
currentLog.addSummary(false, null);
385         }
386     }
387
388     Database getDatabase() {
389         return database;
390     }
391
392     DataPage getRowBuffer() {
393         return rowBuff;
394     }
395
396     public void setFlushOnEachCommit(boolean b) {
397         flushOnEachCommit = b;
398     }
399
400     boolean getFlushOnEachCommit() {
401         return flushOnEachCommit;
402     }
403
404     public void sync() throws SQLException JavaDoc {
405         synchronized (database) {
406             if (currentLog != null) {
407                 currentLog.flush();
408                 currentLog.sync();
409             }
410         }
411     }
412
413     public void setDisabled(boolean disabled) {
414         this.disabled = disabled;
415     }
416
417     void addRedoLog(Storage storage, int recordId, int blockCount, DataPage rec) throws SQLException JavaDoc {
418         DiskFile file = storage.getDiskFile();
419         file.addRedoLog(storage, recordId, blockCount, rec);
420     }
421
422     public void invalidateIndexSummary() throws SQLException JavaDoc {
423         currentLog.addSummary(false, null);
424     }
425
426 }
427
Popular Tags