提交 cd27542d authored 作者: Thomas Mueller's avatar Thomas Mueller

When killing the process while the database was writing a checkpoint or while it…

When killing the process while the database was writing a checkpoint or while it was closing, the database could become corrupt. (work in progress)
上级 740f6da2
...@@ -268,6 +268,10 @@ public class PageLog { ...@@ -268,6 +268,10 @@ public class PageLog {
store.writePage(pageId, data); store.writePage(pageId, data);
undo.set(pageId); undo.set(pageId);
undoAll.set(pageId); undoAll.set(pageId);
} else {
if (trace.isDebugEnabled()) {
trace.debug("log undo skip " + pageId);
}
} }
} }
} else if (x == ADD) { } else if (x == ADD) {
...@@ -292,11 +296,15 @@ public class PageLog { ...@@ -292,11 +296,15 @@ public class PageLog {
int sessionId = in.readVarInt(); int sessionId = in.readVarInt();
int tableId = in.readVarInt(); int tableId = in.readVarInt();
long key = in.readVarLong(); long key = in.readVarLong();
if (stage == RECOVERY_STAGE_UNDO && tableId == -1) { int todo;
// immediately commit, // can not commit immediately
// because the pages may be re-used // because the index root may have started to be moved,
setLastCommitForSession(sessionId, logId, pos); // without arriving at the destination yet
} // if (stage == RECOVERY_STAGE_UNDO && tableId == -1) {
// // immediately commit,
// // because the pages may be re-used
// setLastCommitForSession(sessionId, logId, pos);
// }
if (stage == RECOVERY_STAGE_REDO) { if (stage == RECOVERY_STAGE_REDO) {
if (isSessionCommitted(sessionId, logId, pos)) { if (isSessionCommitted(sessionId, logId, pos)) {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
......
...@@ -35,6 +35,7 @@ import org.h2.index.PageDelegateIndex; ...@@ -35,6 +35,7 @@ import org.h2.index.PageDelegateIndex;
import org.h2.index.PageIndex; import org.h2.index.PageIndex;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.schema.Schema; import org.h2.schema.Schema;
...@@ -150,7 +151,7 @@ public class PageStore implements CacheWriter { ...@@ -150,7 +151,7 @@ public class PageStore implements CacheWriter {
private String fileName; private String fileName;
private FileStore file; private FileStore file;
private String accessMode; private String accessMode;
private int pageSize; private int pageSize = SysProperties.PAGE_SIZE;
private int pageSizeShift; private int pageSizeShift;
private long writeCountBase, writeCount, readCount; private long writeCountBase, writeCount, readCount;
private int logKey, logFirstTrunkPage, logFirstDataPage; private int logKey, logFirstTrunkPage, logFirstDataPage;
...@@ -218,8 +219,9 @@ public class PageStore implements CacheWriter { ...@@ -218,8 +219,9 @@ public class PageStore implements CacheWriter {
this.accessMode = accessMode; this.accessMode = accessMode;
this.database = database; this.database = database;
trace = database.getTrace(Trace.PAGE_STORE); trace = database.getTrace(Trace.PAGE_STORE);
// int test; int test;
// trace.setLevel(TraceSystem.DEBUG); //if (!fileName.endsWith("reopen.h2.db"))
//trace.setLevel(TraceSystem.DEBUG);
String cacheType = database.getCacheType(); String cacheType = database.getCacheType();
this.cache = CacheLRU.getCache(this, cacheType, cacheSizeDefault); this.cache = CacheLRU.getCache(this, cacheType, cacheSizeDefault);
systemSession = new Session(database, null, 0); systemSession = new Session(database, null, 0);
...@@ -253,7 +255,11 @@ public class PageStore implements CacheWriter { ...@@ -253,7 +255,11 @@ public class PageStore implements CacheWriter {
try { try {
metaRootPageId.put(META_TABLE_ID, PAGE_ID_META_ROOT); metaRootPageId.put(META_TABLE_ID, PAGE_ID_META_ROOT);
if (IOUtils.exists(fileName)) { if (IOUtils.exists(fileName)) {
if (IOUtils.length(fileName) < MIN_PAGE_COUNT * PAGE_SIZE_MIN) { long length = IOUtils.length(fileName);
if (length < MIN_PAGE_COUNT * PAGE_SIZE_MIN) {
if (database.isReadOnly()) {
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, fileName + " length: " + length);
}
// the database was not fully created // the database was not fully created
openNew(); openNew();
} else { } else {
...@@ -269,7 +275,7 @@ public class PageStore implements CacheWriter { ...@@ -269,7 +275,7 @@ public class PageStore implements CacheWriter {
} }
private void openNew() { private void openNew() {
setPageSize(SysProperties.PAGE_SIZE); setPageSize(pageSize);
freeListPagesPerList = PageFreeList.getPagesAddressed(pageSize); freeListPagesPerList = PageFreeList.getPagesAddressed(pageSize);
file = database.openFile(fileName, accessMode, false); file = database.openFile(fileName, accessMode, false);
recoveryRunning = true; recoveryRunning = true;
...@@ -292,7 +298,10 @@ public class PageStore implements CacheWriter { ...@@ -292,7 +298,10 @@ public class PageStore implements CacheWriter {
fileLength = file.length(); fileLength = file.length();
pageCount = (int) (fileLength / pageSize); pageCount = (int) (fileLength / pageSize);
if (pageCount < MIN_PAGE_COUNT) { if (pageCount < MIN_PAGE_COUNT) {
close(); if (database.isReadOnly()) {
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, fileName + " pageCount: " + pageCount);
}
IOUtils.delete(fileName);
openNew(); openNew();
return; return;
} }
...@@ -336,12 +345,26 @@ public class PageStore implements CacheWriter { ...@@ -336,12 +345,26 @@ public class PageStore implements CacheWriter {
synchronized (database) { synchronized (database) {
database.checkPowerOff(); database.checkPowerOff();
writeIndexRowCounts(); writeIndexRowCounts();
writeBack();
log.checkpoint(); int test;
// writeBack();
// log.checkpoint();
log.checkpoint();
writeBack();
int firstUncommittedSection = getFirstUncommittedSection(); int firstUncommittedSection = getFirstUncommittedSection();
log.removeUntil(firstUncommittedSection); log.removeUntil(firstUncommittedSection);
// write back the free list // write back the free list
writeBack(); writeBack();
int test2;
// ensure the free list is backed up again
log.checkpoint();
byte[] empty = new byte[pageSize]; byte[] empty = new byte[pageSize];
for (int i = PAGE_ID_FREE_LIST_ROOT; i < pageCount; i++) { for (int i = PAGE_ID_FREE_LIST_ROOT; i < pageCount; i++) {
if (isUsed(i)) { if (isUsed(i)) {
...@@ -385,6 +408,10 @@ public class PageStore implements CacheWriter { ...@@ -385,6 +408,10 @@ public class PageStore implements CacheWriter {
logFirstTrunkPage = lastUsed + 1; logFirstTrunkPage = lastUsed + 1;
allocatePage(logFirstTrunkPage); allocatePage(logFirstTrunkPage);
log.openForWriting(logFirstTrunkPage, true); log.openForWriting(logFirstTrunkPage, true);
// ensure the free list is backed up again
log.checkpoint();
} finally { } finally {
recoveryRunning = false; recoveryRunning = false;
} }
...@@ -402,8 +429,23 @@ public class PageStore implements CacheWriter { ...@@ -402,8 +429,23 @@ public class PageStore implements CacheWriter {
break; break;
} }
} }
this.checkpoint();
int test;
log.checkpoint();
writeIndexRowCounts(); writeIndexRowCounts();
log.checkpoint();
writeBack(); writeBack();
int test3;
commit(systemSession);
writeBack();
log.checkpoint();
// truncate the log // truncate the log
recoveryRunning = true; recoveryRunning = true;
try { try {
...@@ -459,9 +501,23 @@ public class PageStore implements CacheWriter { ...@@ -459,9 +501,23 @@ public class PageStore implements CacheWriter {
} finally { } finally {
changeCount++; changeCount++;
} }
if (log.getLogSectionId() == logSection || log.getLogPos() != logPos) { //if (log.getLogSectionId() != logSection || log.getLogPos() != logPos) {
commit(systemSession); // // commit if an index root page moved
} //int test;
//// need to write the log first, then the moved
//// need to write the moved root node,
//// and then we can commit
////log.checkpoint();
//writeBack();
//// commit(systemSession);
//
//int testForceProblem;
//log.checkpoint();
//writeBack();
//log.checkpoint();
//writeBack();
//
// }
} else { } else {
freePage(full); freePage(full);
} }
...@@ -613,9 +669,9 @@ public class PageStore implements CacheWriter { ...@@ -613,9 +669,9 @@ public class PageStore implements CacheWriter {
* *
* @param size the page size * @param size the page size
*/ */
private void setPageSize(int size) { public void setPageSize(int size) {
if (size < PAGE_SIZE_MIN || size > PAGE_SIZE_MAX) { if (size < PAGE_SIZE_MIN || size > PAGE_SIZE_MAX) {
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, fileName); throw DbException.get(ErrorCode.FILE_CORRUPTED_1, fileName + " pageSize: " + size);
} }
boolean good = false; boolean good = false;
int shift = 0; int shift = 0;
...@@ -663,6 +719,7 @@ public class PageStore implements CacheWriter { ...@@ -663,6 +719,7 @@ public class PageStore implements CacheWriter {
} }
private void writeVariableHeader() { private void writeVariableHeader() {
trace.debug("writeVariableHeader");
file.sync(); file.sync();
Data page = createData(); Data page = createData();
page.writeInt(0); page.writeInt(0);
...@@ -1292,7 +1349,7 @@ public class PageStore implements CacheWriter { ...@@ -1292,7 +1349,7 @@ public class PageStore implements CacheWriter {
String[] ops = StringUtils.arraySplit(options, ',', false); String[] ops = StringUtils.arraySplit(options, ',', false);
Index meta; Index meta;
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("addMeta id=" + id + " type=" + type + " parent=" + parent + " columns=" + columnList); trace.debug("addMeta id=" + id + " type=" + type + " root=" + rootPageId + " parent=" + parent + " columns=" + columnList);
} }
if (redo && rootPageId != 0) { if (redo && rootPageId != 0) {
// ensure the page is empty, but not used by regular data // ensure the page is empty, but not used by regular data
......
...@@ -32,6 +32,7 @@ public class WriterThread implements Runnable { ...@@ -32,6 +32,7 @@ public class WriterThread implements Runnable {
private volatile WeakReference<Database> databaseRef; private volatile WeakReference<Database> databaseRef;
private int writeDelay; private int writeDelay;
private Thread thread;
private volatile boolean stop; private volatile boolean stop;
private WriterThread(Database database, int writeDelay) { private WriterThread(Database database, int writeDelay) {
...@@ -59,10 +60,9 @@ public class WriterThread implements Runnable { ...@@ -59,10 +60,9 @@ public class WriterThread implements Runnable {
public static WriterThread create(Database database, int writeDelay) { public static WriterThread create(Database database, int writeDelay) {
try { try {
WriterThread writer = new WriterThread(database, writeDelay); WriterThread writer = new WriterThread(database, writeDelay);
Thread thread = new Thread(writer); writer.thread = new Thread(writer);
thread.setName("H2 Log Writer " + database.getShortName()); writer.thread.setName("H2 Log Writer " + database.getShortName());
thread.setDaemon(true); writer.thread.setDaemon(true);
thread.start();
return writer; return writer;
} catch (AccessControlException e) { } catch (AccessControlException e) {
// // Google App Engine does not allow threads // // Google App Engine does not allow threads
...@@ -113,4 +113,13 @@ public class WriterThread implements Runnable { ...@@ -113,4 +113,13 @@ public class WriterThread implements Runnable {
stop = true; stop = true;
} }
/**
* Start the thread. This method is called after opening the database
* (to avoid deadlocks)
*/
public void startThread() {
thread.start();
this.thread = null;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论