?? logsystem.java
字號:
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.log;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.HashMap;
import org.h2.api.DatabaseEventListener;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Trace;
import org.h2.store.DataPage;
import org.h2.store.DiskFile;
import org.h2.store.Record;
import org.h2.store.Storage;
import org.h2.util.FileUtils;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
/**
* The transaction log system is responsible for the write ahead log mechanism
* used in this database. A number of {@link LogFile} objects are used (one for
* each file).
*/
public class LogSystem {
public static final int LOG_WRITTEN = -1;
private Database database;
private ObjectArray activeLogs;
private LogFile currentLog;
private String fileNamePrefix;
private HashMap storages = new HashMap();
private HashMap sessions = new HashMap();
private DataPage rowBuff;
private ObjectArray undo;
// TODO log file / deleteOldLogFilesAutomatically:
// make this a setting, so they can be backed up
private boolean deleteOldLogFilesAutomatically = true;
private long maxLogSize = Constants.DEFAULT_MAX_LOG_SIZE;
private boolean readOnly;
private boolean flushOnEachCommit;
private ObjectArray inDoubtTransactions;
private boolean disabled;
private int keepFiles;
private boolean closed;
private String accessMode;
public LogSystem(Database database, String fileNamePrefix, boolean readOnly, String accessMode) throws SQLException {
this.database = database;
this.readOnly = readOnly;
this.accessMode = accessMode;
closed = true;
if (database == null) {
return;
}
this.fileNamePrefix = fileNamePrefix;
rowBuff = DataPage.create(database, Constants.DEFAULT_DATA_PAGE_SIZE);
}
public void setMaxLogSize(long maxSize) {
this.maxLogSize = maxSize;
}
public long getMaxLogSize() {
return maxLogSize;
}
public boolean containsInDoubtTransactions() {
return inDoubtTransactions != null && inDoubtTransactions.size() > 0;
}
private void flushAndCloseUnused() throws SQLException {
currentLog.flush();
DiskFile file = database.getDataFile();
if (file == null) {
return;
}
file.flush();
if (containsInDoubtTransactions()) {
// if there are any in-doubt transactions
// (even if they are resolved), can't update or delete the log files
return;
}
Session[] sessions = database.getSessions();
int firstUncommittedLog = currentLog.getId();
int firstUncommittedPos = currentLog.getPos();
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
int log = session.getFirstUncommittedLog();
int pos = session.getFirstUncommittedPos();
if (pos != LOG_WRITTEN) {
if (log < firstUncommittedLog || (log == firstUncommittedLog && pos < firstUncommittedPos)) {
firstUncommittedLog = log;
firstUncommittedPos = pos;
}
}
}
for (int i = activeLogs.size() - 1; i >= 0; i--) {
LogFile l = (LogFile) activeLogs.get(i);
if (l.getId() < firstUncommittedLog) {
l.setFirstUncommittedPos(LOG_WRITTEN);
} else if (l.getId() == firstUncommittedLog) {
if (firstUncommittedPos == l.getPos()) {
l.setFirstUncommittedPos(LOG_WRITTEN);
} else {
l.setFirstUncommittedPos(firstUncommittedPos);
}
}
}
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
if (l.getFirstUncommittedPos() == LOG_WRITTEN) {
// must remove the log file first
// if we don't do that, the file is closed but still in the list
activeLogs.remove(i);
i--;
closeOldFile(l);
}
}
}
public void close() throws SQLException {
if (database == null) {
return;
}
synchronized (database) {
if (closed) {
return;
}
if (readOnly) {
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
l.close(false);
}
closed = true;
return;
}
// TODO refactor flushing and closing files when we know what to do exactly
SQLException closeException = null;
try {
flushAndCloseUnused();
if (!containsInDoubtTransactions()) {
checkpoint();
}
} catch (SQLException e) {
closeException = e;
}
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
try {
// if there are any in-doubt transactions
// (even if they are resolved), can't delete the log files
if (l.getFirstUncommittedPos() == LOG_WRITTEN && !containsInDoubtTransactions()) {
closeOldFile(l);
} else {
l.close(false);
}
} catch (SQLException e) {
// TODO log exception
if (closeException == null) {
closeException = e;
}
}
}
closed = true;
if (closeException != null) {
throw closeException;
}
}
}
void addUndoLogRecord(LogFile log, int logRecordId, int sessionId) {
getOrAddSessionState(sessionId);
LogRecord record = new LogRecord(log, logRecordId, sessionId);
undo.add(record);
}
public boolean recover() throws SQLException {
if (database == null) {
return false;
}
synchronized (database) {
if (closed) {
return false;
}
undo = new ObjectArray();
for (int i = 0; i < activeLogs.size(); i++) {
LogFile log = (LogFile) activeLogs.get(i);
log.redoAllGoEnd();
}
database.getDataFile().flushRedoLog();
database.getIndexFile().flushRedoLog();
int end = currentLog.getPos();
Object[] states = sessions.values().toArray();
inDoubtTransactions = new ObjectArray();
for (int i = 0; i < states.length; i++) {
SessionState state = (SessionState) states[i];
if (state.inDoubtTransaction != null) {
inDoubtTransactions.add(state.inDoubtTransaction);
}
}
for (int i = undo.size() - 1; i >= 0 && sessions.size() > 0; i--) {
database.setProgress(DatabaseEventListener.STATE_RECOVER, null, undo.size() - 1 - i, undo.size());
LogRecord record = (LogRecord) undo.get(i);
if (sessions.get(ObjectUtils.getInteger(record.sessionId)) != null) {
// undo only if the session is not yet committed
record.log.undo(record.logRecordId);
database.getDataFile().flushRedoLog();
database.getIndexFile().flushRedoLog();
}
}
currentLog.go(end);
boolean fileChanged = undo.size() > 0;
undo = null;
storages.clear();
if (!readOnly && fileChanged && !containsInDoubtTransactions()) {
checkpoint();
}
return fileChanged;
}
}
private void closeOldFile(LogFile l) throws SQLException {
l.close(deleteOldLogFilesAutomatically && keepFiles == 0);
}
public void open() throws SQLException {
String path = FileUtils.getParent(fileNamePrefix);
String[] list = FileUtils.listFiles(path);
activeLogs = new ObjectArray();
for (int i = 0; i < list.length; i++) {
String s = list[i];
LogFile l = null;
try {
l = LogFile.openIfLogFile(this, fileNamePrefix, s);
} catch (SQLException e) {
database.getTrace(Trace.LOG).debug("Error opening log file, header corrupt: "+s, e);
// this can happen if the system crashes just
// after creating a new file (before writing the header)
// rename it, so that it doesn't get in the way the next time
FileUtils.delete(s + ".corrupt");
FileUtils.rename(s, s + ".corrupt");
}
if (l != null) {
if (l.getPos() == LOG_WRITTEN) {
closeOldFile(l);
} else {
activeLogs.add(l);
}
}
}
activeLogs.sort(new Comparator() {
public int compare(Object a, Object b) {
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -