提交 6dd1a3f4 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)
上级 77cc7372
...@@ -22,9 +22,9 @@ Change Log ...@@ -22,9 +22,9 @@ Change Log
</li><li>Translation: Lubomir Grajciar translated the H2 Console as well as all error message to Slovensky. Thanks a lot! </li><li>Translation: Lubomir Grajciar translated the H2 Console as well as all error message to Slovensky. Thanks a lot!
</li><li>There was a possible Java level deadlock when opening an uninitialized database and using </li><li>There was a possible Java level deadlock when opening an uninitialized database and using
a file system that also opened a database. a file system that also opened a database.
</li><li>When killing the process while the database was writing a checkpoint or while it was closing, </li><li>When killing the process while the database was writing a checkpoint, while it was closing,
the database could become corrupt. A new test case has been implemented to ensure such or while running recovery (while removing temporary tables from a previous run), the database could become corrupt.
problems can not occur in the future. A new test case has been implemented to ensure such problems can not occur in the future.
</li><li>File system: new method FileSystem.setReadOnly. </li><li>File system: new method FileSystem.setReadOnly.
</li><li>The page size for new databases can now be set in the database URL using ;PAGE_SIZE=512. </li><li>The page size for new databases can now be set in the database URL using ;PAGE_SIZE=512.
Currently this feature is only used to simplify testing. Currently this feature is only used to simplify testing.
......
...@@ -464,6 +464,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>. ...@@ -464,6 +464,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
(don't store / read it). This may increase write / read performance depending on the file system. (don't store / read it). This may increase write / read performance depending on the file system.
</li><li>Indexes of temporary tables are currently kept in-memory. Is this how it should be? </li><li>Indexes of temporary tables are currently kept in-memory. Is this how it should be?
</li><li>The Shell tool should support the same built-in commands as the H2 Console. </li><li>The Shell tool should support the same built-in commands as the H2 Console.
</li><li>Maybe use PhantomReference instead of finalize.
</li></ul> </li></ul>
<h2>Not Planned</h2> <h2>Not Planned</h2>
......
...@@ -305,6 +305,10 @@ public class PageDataLeaf extends PageData { ...@@ -305,6 +305,10 @@ public class PageDataLeaf extends PageData {
* @return the row * @return the row
*/ */
Row getRowAt(int at) { Row getRowAt(int at) {
int test;
if (rows == null || rows.length == 0) {
System.out.println("stop " + getPos());
}
Row r = rows[at]; Row r = rows[at];
if (r == null) { if (r == null) {
if (firstOverflowPageId == 0) { if (firstOverflowPageId == 0) {
...@@ -372,6 +376,10 @@ public class PageDataLeaf extends PageData { ...@@ -372,6 +376,10 @@ public class PageDataLeaf extends PageData {
return null; return null;
} }
PageDataNode next = (PageDataNode) index.getPage(parentPageId, -1); PageDataNode next = (PageDataNode) index.getPage(parentPageId, -1);
int test;
if (next == null || keys == null) {
System.out.println("stop " + getPos());
}
return next.getNextPage(keys[entryCount - 1]); return next.getNextPage(keys[entryCount - 1]);
} }
......
...@@ -179,6 +179,7 @@ public class PageStore implements CacheWriter { ...@@ -179,6 +179,7 @@ public class PageStore implements CacheWriter {
private PageDataIndex metaIndex; private PageDataIndex metaIndex;
private IntIntHashMap metaRootPageId = new IntIntHashMap(); private IntIntHashMap metaRootPageId = new IntIntHashMap();
private HashMap<Integer, PageIndex> metaObjects = New.hashMap(); private HashMap<Integer, PageIndex> metaObjects = New.hashMap();
private ArrayList<PageIndex> tempIndexes = New.arrayList();
/** /**
* The map of reserved pages, to ensure index head pages * The map of reserved pages, to ensure index head pages
...@@ -316,9 +317,23 @@ int test; ...@@ -316,9 +317,23 @@ int test;
log.openForWriting(logFirstTrunkPage, false); log.openForWriting(logFirstTrunkPage, false);
recoveryRunning = false; recoveryRunning = false;
checkpoint(); checkpoint();
removeOldTempIndexes();
} }
} }
private void removeOldTempIndexes() {
for (PageIndex index: tempIndexes) {
index.truncate(systemSession);
index.remove(systemSession);
}
if (tempIndexes.size() > 0) {
systemSession.commit(true);
}
metaObjects.clear();
metaObjects.put(-1, metaIndex);
tempIndexes = null;
}
private void writeIndexRowCounts() { private void writeIndexRowCounts() {
for (PageIndex index: metaObjects.values()) { for (PageIndex index: metaObjects.values()) {
index.writeRowCount(); index.writeRowCount();
...@@ -1114,29 +1129,52 @@ log.checkpoint(); ...@@ -1114,29 +1129,52 @@ log.checkpoint();
setReadOnly = true; setReadOnly = true;
} }
} }
// remove temp tables
// PageDataIndex systemTable = (PageDataIndex) metaObjects.get(0);
// isNew = systemTable == null;
// for (Iterator<PageIndex> it = metaObjects.values().iterator(); it.hasNext();) {
// Index openIndex = it.next();
// if (openIndex.getTable().isTemporary()) {
// openIndex.truncate(systemSession);
// openIndex.remove(systemSession);
// removeMetaIndex(openIndex, systemSession);
// it.remove();
// } else {
// openIndex.close(systemSession);
// }
// }
PageDataIndex systemTable = (PageDataIndex) metaObjects.get(0); PageDataIndex systemTable = (PageDataIndex) metaObjects.get(0);
isNew = systemTable == null; isNew = systemTable == null;
for (Iterator<PageIndex> it = metaObjects.values().iterator(); it.hasNext();) { for (Iterator<PageIndex> it = metaObjects.values().iterator(); it.hasNext();) {
Index openIndex = it.next(); PageIndex openIndex = it.next();
if (openIndex.getTable().isTemporary()) { if (openIndex.getTable().isTemporary()) {
openIndex.truncate(systemSession); tempIndexes.add(openIndex);
openIndex.remove(systemSession); // System.out.println("temp: " + openIndex);
removeMetaIndex(openIndex, systemSession);
it.remove();
} else { } else {
openIndex.close(systemSession); openIndex.close(systemSession);
} }
int test;
} }
allocatePage(PAGE_ID_META_ROOT); allocatePage(PAGE_ID_META_ROOT);
writeIndexRowCounts(); writeIndexRowCounts();
recoveryRunning = false; recoveryRunning = false;
reservedPages = null; reservedPages = null;
writeBack(); writeBack();
// clear the cache because it contains pages with closed indexes // clear the cache because it contains pages with closed indexes
cache.clear(); cache.clear();
freeLists.clear(); freeLists.clear();
metaObjects.clear(); metaObjects.clear();
metaObjects.put(-1, metaIndex); metaObjects.put(-1, metaIndex);
int test;
for (PageIndex index : tempIndexes) {
metaObjects.put(index.getId(), index);
}
if (setReadOnly) { if (setReadOnly) {
database.setReadOnly(true); database.setReadOnly(true);
} }
...@@ -1318,14 +1356,17 @@ log.checkpoint(); ...@@ -1318,14 +1356,17 @@ log.checkpoint();
PageIndex index = metaObjects.get(id); PageIndex index = metaObjects.get(id);
int rootPageId = index.getRootPageId(); int rootPageId = index.getRootPageId();
index.getTable().removeIndex(index); index.getTable().removeIndex(index);
if (index instanceof PageBtreeIndex) {
int test;
// if (index instanceof PageBtreeIndex) {
if (index instanceof PageBtreeIndex || index instanceof PageDelegateIndex) {
if (index.isTemporary()) { if (index.isTemporary()) {
systemSession.removeLocalTempTableIndex(index); systemSession.removeLocalTempTableIndex(index);
} else { } else {
index.getSchema().remove(index); index.getSchema().remove(index);
} }
} else if (index instanceof PageDelegateIndex) { // } else if (index instanceof PageDelegateIndex) {
index.getSchema().remove(index); // index.getSchema().remove(index);
} }
index.remove(systemSession); index.remove(systemSession);
metaObjects.remove(id); metaObjects.remove(id);
......
...@@ -39,11 +39,11 @@ public class TestPageStoreCoverage extends TestBase { ...@@ -39,11 +39,11 @@ public class TestPageStoreCoverage extends TestBase {
return; return;
} }
deleteDb("pageStore"); deleteDb("pageStore");
// testMoveRoot(); testMoveRoot();
// testBasic(); testBasic();
// testReadOnly(); testReadOnly();
// testIncompleteCreate(); testIncompleteCreate();
// testBackupRestore(); testBackupRestore();
testTrim(); testTrim();
testLongTransaction(); testLongTransaction();
testRecoverTemp(); testRecoverTemp();
......
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
*/ */
package org.h2.test.unit; package org.h2.test.unit;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
...@@ -15,11 +13,11 @@ import org.h2.constant.ErrorCode; ...@@ -15,11 +13,11 @@ import org.h2.constant.ErrorCode;
import org.h2.engine.ConnectionInfo; import org.h2.engine.ConnectionInfo;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.message.DbException;
import org.h2.store.fs.FileSystem; import org.h2.store.fs.FileSystem;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.utils.Recorder; import org.h2.test.utils.Recorder;
import org.h2.test.utils.RecordingFileSystem; import org.h2.test.utils.RecordingFileSystem;
import org.h2.util.JdbcUtils;
import org.h2.util.New; import org.h2.util.New;
/** /**
...@@ -31,7 +29,7 @@ public class TestReopen extends TestBase implements Recorder { ...@@ -31,7 +29,7 @@ public class TestReopen extends TestBase implements Recorder {
private String testDatabase = "memFS:" + TestBase.BASE_TEST_DIR + "/reopen"; private String testDatabase = "memFS:" + TestBase.BASE_TEST_DIR + "/reopen";
private long lastCheck; private long lastCheck;
private int counter; private int counter;
private int testEvery = 1 << 10; private int testEvery = 1 << 4;
private HashSet<String> knownErrors = New.hashSet(); private HashSet<String> knownErrors = New.hashSet();
private int max = 103128; private int max = 103128;
...@@ -88,6 +86,15 @@ System.out.println(System.currentTimeMillis() - time); ...@@ -88,6 +86,15 @@ System.out.println(System.currentTimeMillis() - time);
database.removeSession(null); database.removeSession(null);
// everything OK - return // everything OK - return
return; return;
} catch (DbException e) {
SQLException e2 = DbException.toSQLException(e);
int errorCode = e2.getErrorCode();
if (errorCode == ErrorCode.WRONG_USER_OR_PASSWORD) {
return;
} else if (errorCode == ErrorCode.FILE_ENCRYPTION_ERROR_1) {
return;
}
e.printStackTrace(System.out);
} catch (Exception e) { } catch (Exception e) {
// failed // failed
int errorCode = 0; int errorCode = 0;
......
...@@ -63,8 +63,8 @@ public class RecordingFileObject implements FileObject { ...@@ -63,8 +63,8 @@ public class RecordingFileObject implements FileObject {
buff = new byte[len]; buff = new byte[len];
System.arraycopy(b, off, buff, 0, len); System.arraycopy(b, off, buff, 0, len);
} }
fs.log(Recorder.WRITE, name, buff, file.getFilePointer());
file.write(b, off, len); file.write(b, off, len);
fs.log(Recorder.WRITE, name, buff, file.getFilePointer());
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论