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

In some cases (specially when using a small cache size or a large database over…

In some cases (specially when using a small cache size or a large database over 1 GB) for some operations like ALTER TABLE the data was written before the transaction log entries, which could cause a corrupt database if the process was killed during the ALTER TABLE operation.
上级 36ddbb47
......@@ -234,9 +234,10 @@ abstract class PageData extends Page {
}
public boolean canRemove() {
if (changeCount >= index.getPageStore().getChangeCount()) {
return false;
}
int test;
// if (changeCount >= index.getPageStore().getChangeCount()) {
// return false;
// }
return true;
}
......
......@@ -158,6 +158,11 @@ public class PageLog {
*/
private BitSet usedLogPages;
/**
* This flag is set while freeing up pages.
*/
private boolean freeing;
PageLog(PageStore store) {
this.store = store;
dataBuffer = store.createData();
......@@ -196,17 +201,31 @@ public class PageLog {
currentDataPage = pageOut.getCurrentDataPageId();
pageOut.freeReserved();
}
PageStreamTrunk.Iterator it = new PageStreamTrunk.Iterator(store, firstTrunkPage);
while (firstTrunkPage != 0 && firstTrunkPage < store.getPageCount()) {
PageStreamTrunk t = it.next();
if (t == null) {
if (it.canDelete()) {
store.free(firstTrunkPage, false);
try {
freeing = true;
int first = 0;
int loopDetect = 1024, loopCount = 0;
PageStreamTrunk.Iterator it = new PageStreamTrunk.Iterator(store, firstTrunkPage);
while (firstTrunkPage != 0 && firstTrunkPage < store.getPageCount()) {
PageStreamTrunk t = it.next();
if (loopCount++ >= loopDetect) {
first = t.getPos();
loopCount = 0;
loopDetect *= 2;
} else if (first != 0 && t != null && first == t.getPos()) {
throw DbException.throwInternalError("endless loop at " + t);
}
if (t == null) {
if (it.canDelete()) {
store.free(firstTrunkPage, false);
}
break;
}
break;
t.free(currentDataPage);
firstTrunkPage = t.getNextTrunk();
}
t.free(currentDataPage);
firstTrunkPage = t.getNextTrunk();
} finally {
freeing = false;
}
}
......@@ -452,7 +471,7 @@ public class PageLog {
* @param page the old page data
*/
void addUndo(int pageId, Data page) {
if (undo.get(pageId)) {
if (undo.get(pageId) || freeing) {
return;
}
if (trace.isDebugEnabled()) {
......
......@@ -168,6 +168,11 @@ public class CacheLRU implements Cache {
}
}
if (changed.size() > 0) {
int fixMaybeReplacesChangeCount;
if (!flushed) {
writer.flushLog();
}
Collections.sort(changed);
int max = maxMemory;
try {
......
......@@ -109,7 +109,7 @@ public class TestCache extends TestBase implements CacheWriter {
for (int i = 0; i < 20; i++) {
c.put(new Obj(i));
}
assertEquals("0 1 2 3 ", out);
assertEquals("flush 0 flush 1 flush 2 flush 3 ", out);
}
/**
......
......@@ -34,10 +34,12 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
* @param a ignored
*/
public static void main(String... a) throws Exception {
System.setProperty("h2.check2", "true");
TestBase.createCaller().init().test();
}
public void test() throws Exception {
testWriteTransactionLogBeforeData();
testDefrag();
testInsertReverse();
testInsertDelete();
......@@ -66,6 +68,36 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
deleteDb("pageStore");
}
private void testWriteTransactionLogBeforeData() throws SQLException {
deleteDb("pageStore");
String url = getURL("pageStore;CACHE_SIZE=16;WRITE_DELAY=1000000", true);
Connection conn;
Statement stat;
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
stat.execute("create table test(name varchar) as select space(100000)");
for (int i = 0; i < 100; i++) {
stat.execute("create table test" + i + "(id int) as select x from system_range(1, 1000)");
}
conn.close();
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
stat.execute("drop table test0");
stat.execute("select * from test");
stat.execute("shutdown immediately");
try {
conn.close();
} catch (Exception e) {
// ignore
}
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
for (int i = 1; i < 100; i++) {
stat.execute("select * from test" + i);
}
conn.close();
}
private void testDefrag() throws SQLException {
deleteDb("pageStore");
Connection conn = getConnection("pageStore;LOG=0;UNDO_LOG=0;LOCK_MODE=0");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论