001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.store;
007:
008: import java.lang.ref.WeakReference;
009: import java.sql.SQLException;
010:
011: import org.h2.constant.SysProperties;
012: import org.h2.engine.Constants;
013: import org.h2.engine.Database;
014: import org.h2.engine.DbObject;
015: import org.h2.index.BtreeIndex;
016: import org.h2.log.LogSystem;
017: import org.h2.message.Trace;
018: import org.h2.message.TraceSystem;
019: import org.h2.table.Table;
020: import org.h2.util.FileUtils;
021: import org.h2.util.ObjectArray;
022:
023: /**
024: * The writer thread is responsible to flush the transaction log file from time
025: * to time.
026: */
027: public class WriterThread extends Thread {
028:
029: /**
030: * The reference to the database.
031: *
032: * Thread objects are not garbage collected
033: * until they returned from the run() method
034: * (even if they where never started)
035: * so if the connection was not closed,
036: * the database object cannot get reclaimed
037: * by the garbage collector if we use a hard reference.
038: */
039: private volatile WeakReference databaseRef;
040:
041: private int writeDelay;
042: private long lastIndexFlush;
043: private volatile boolean stop;
044: private long oldLogFileDelete;
045: private String oldLogFile;
046:
047: private WriterThread(Database database, int writeDelay) {
048: this .databaseRef = new WeakReference(database);
049: this .writeDelay = writeDelay;
050: }
051:
052: public void setWriteDelay(int writeDelay) {
053: LogSystem log = getLog();
054: this .writeDelay = writeDelay;
055: // TODO check if MIN_WRITE_DELAY is a good value
056: if (writeDelay < SysProperties.MIN_WRITE_DELAY) {
057: log.setFlushOnEachCommit(true);
058: } else {
059: log.setFlushOnEachCommit(false);
060: }
061: }
062:
063: public static WriterThread create(Database database, int writeDelay) {
064: WriterThread thread = new WriterThread(database, writeDelay);
065: thread.setName("H2 Log Writer " + database.getShortName());
066: thread.setDaemon(true);
067: thread.start();
068: return thread;
069: }
070:
071: private LogSystem getLog() {
072: Database database = (Database) databaseRef.get();
073: if (database == null) {
074: return null;
075: }
076: LogSystem log = database.getLog();
077: return log;
078: }
079:
080: private void flushIndexes(Database database) {
081: long time = System.currentTimeMillis();
082: if (lastIndexFlush + Constants.FLUSH_INDEX_DELAY > time) {
083: return;
084: }
085: synchronized (database) {
086: ObjectArray array = database
087: .getAllSchemaObjects(DbObject.INDEX);
088: for (int i = 0; i < array.size(); i++) {
089: DbObject obj = (DbObject) array.get(i);
090: if (obj instanceof BtreeIndex) {
091: BtreeIndex idx = (BtreeIndex) obj;
092: if (idx.getLastChange() == 0) {
093: continue;
094: }
095: Table tab = idx.getTable();
096: if (tab.isLockedExclusively()) {
097: continue;
098: }
099: if (idx.getLastChange()
100: + Constants.FLUSH_INDEX_DELAY > time) {
101: continue;
102: }
103: try {
104: idx.flush(database.getSystemSession());
105: } catch (SQLException e) {
106: database.getTrace(Trace.DATABASE).error(
107: "flush index " + idx.getName(), e);
108: }
109: }
110: }
111: }
112: lastIndexFlush = time;
113: }
114:
115: public void run() {
116: while (!stop) {
117: synchronized (this ) {
118: if (oldLogFile != null) {
119: long time = System.currentTimeMillis();
120: if (time > oldLogFileDelete) {
121: FileUtils.tryDelete(oldLogFile);
122: if (!FileUtils.exists(oldLogFile)) {
123: oldLogFile = null;
124: }
125: }
126: }
127: }
128: Database database = (Database) databaseRef.get();
129: if (database == null) {
130: break;
131: }
132: if (Constants.FLUSH_INDEX_DELAY != 0) {
133: flushIndexes(database);
134: }
135: LogSystem log = database.getLog();
136: if (log == null) {
137: break;
138: }
139: try {
140: log.flush();
141: } catch (SQLException e) {
142: TraceSystem traceSystem = database.getTraceSystem();
143: if (traceSystem != null) {
144: traceSystem.getTrace(Trace.LOG).error("flush", e);
145: }
146: }
147: // TODO log writer: could also flush the dirty cache when there is
148: // low activity
149: // wait 0 mean wait forever
150: int wait = writeDelay > 0 ? writeDelay : 1;
151: try {
152: Thread.sleep(wait);
153: } catch (InterruptedException e) {
154: // ignore
155: }
156: }
157: databaseRef = null;
158: }
159:
160: public void stopThread() throws SQLException {
161: stop = true;
162: deleteLogFileLater(null);
163: }
164:
165: public synchronized void deleteLogFileLater(String fileName)
166: throws SQLException {
167: if (oldLogFile != null) {
168: FileUtils.delete(oldLogFile);
169: }
170: oldLogFile = fileName;
171: if (fileName != null) {
172: oldLogFileDelete = System.currentTimeMillis()
173: + SysProperties.getLogFileDeleteDelay();
174: }
175: }
176:
177: }
|