⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 logsystem.java

📁 非常棒的java数据库
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * 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) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -