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

New experimental page store.

上级 a18e7177
...@@ -15,58 +15,56 @@ import org.h2.util.IntArray; ...@@ -15,58 +15,56 @@ import org.h2.util.IntArray;
/** /**
* The list of free pages of a page store. * The list of free pages of a page store.
* The format of a free list trunk page is: * The format of a free list trunk page is:
* <ul><li>0-3: parent page id (0 for head) * <ul><li>0-3: parent page id (always 0)
* </li><li>4-4: page type * </li><li>4-4: page type
* </li><li>5-8: the next page (if there are more) or number of entries * </li><li>5-8: the next page (if there are more) or number of entries
* </li><li>9-remainder: data (4 bytes each entry) * </li><li>9-remainder: data (4 bytes each entry)
* </li></ul> * </li></ul>
*/ */
public class PageFreeList { public class PageFreeList extends Record {
private final PageStore store; private final PageStore store;
private DataPage page; private final DataPage page;
private int pageId; private final IntArray array = new IntArray();
private int nextPage; private int nextPage;
private IntArray array = new IntArray();
PageFreeList(PageStore store, int pageId) throws SQLException { PageFreeList(PageStore store, int pageId, int nextPage) {
setPos(pageId);
this.page = store.createDataPage();
this.store = store; this.store = store;
readTrunk(pageId); this.nextPage = nextPage;
int maybeWorkLikeAStack;
int alsoReturnTrunkPagesOnceTheyAreEmpty;
} }
int allocate() throws SQLException { int allocate() throws SQLException {
while (true) {
int size = array.size(); int size = array.size();
if (size > 0) { if (size > 0) {
int x = array.get(size - 1); int x = array.get(size - 1);
array.remove(size - 1); array.remove(size - 1);
return x; return x;
} }
if (nextPage == 0) { store.updateRecord(this);
return -1; // no more free pages in this list:
} // set the next page (may be 0, meaning no free pages)
readTrunk(nextPage); store.setFreeListRootPage(nextPage, true, 0);
} // and then return the page itself
return getPos();
} }
private void readTrunk(int pageId) throws SQLException { private int getMaxSize() {
if (nextPage == 0) { return (store.getPageSize() - 9) / DataPage.LENGTH_INT;
return;
} }
int parentPage = pageId;
pageId = nextPage; void read() throws SQLException {
store.readPage(pageId, page); store.readPage(getPos(), page);
int p = page.readInt(); int p = page.readInt();
int t = page.readByte(); int t = page.readByte();
boolean last = (t & Page.FLAG_LAST) != 0; boolean last = (t & Page.FLAG_LAST) != 0;
t &= ~Page.FLAG_LAST; t &= ~Page.FLAG_LAST;
if (t != Page.TYPE_FREE_LIST || p != parentPage) { if (t != Page.TYPE_FREE_LIST || p != 0) {
throw Message.getSQLException( throw Message.getSQLException(
ErrorCode.FILE_CORRUPTED_1, ErrorCode.FILE_CORRUPTED_1,
"type:" + t + " parent:" + p + "type:" + t + " parent:" + p +
" expected type:" + Page.TYPE_FREE_LIST + " expected parent:" + parentPage); " expected type:" + Page.TYPE_FREE_LIST);
} }
int size; int size;
if (last) { if (last) {
...@@ -74,15 +72,46 @@ public class PageFreeList { ...@@ -74,15 +72,46 @@ public class PageFreeList {
size = page.readInt(); size = page.readInt();
} else { } else {
nextPage = page.readInt(); nextPage = page.readInt();
size = (store.getPageSize() - page.length()) / DataPage.LENGTH_INT; size = getMaxSize();
} }
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
array.add(page.readInt()); array.add(page.readInt());
} }
} }
void free(int pageId) { void free(int pageId) throws SQLException {
store.updateRecord(this);
if (array.size() < getMaxSize()) {
array.add(pageId);
} else {
// this page is full:
// the freed page is the next list
this.nextPage = pageId;
// set the next page
store.setFreeListRootPage(pageId, false, getPos());
}
}
public int getByteCount(DataPage dummy) throws SQLException {
return store.getPageSize();
}
public void write(DataPage buff) throws SQLException {
page.reset();
page.writeInt(0);
int type = Page.TYPE_FREE_LIST;
if (nextPage == 0) {
type |= Page.FLAG_LAST;
}
page.writeByte((byte) type);
if (nextPage != 0) {
page.writeInt(nextPage);
} else {
page.writeInt(array.size());
}
for (int i = 0; i < array.size(); i++) {
page.writeInt(array.get(i));
}
} }
} }
...@@ -56,6 +56,10 @@ public class PageStore implements CacheWriter { ...@@ -56,6 +56,10 @@ public class PageStore implements CacheWriter {
private int systemRootPageId; private int systemRootPageId;
private int freeListRootPageId; private int freeListRootPageId;
private int freePageCount; private int freePageCount;
/**
* Number of pages (including free pages).
*/
private int pageCount; private int pageCount;
private int writeCount; private int writeCount;
private long fileLength; private long fileLength;
...@@ -242,6 +246,9 @@ public class PageStore implements CacheWriter { ...@@ -242,6 +246,9 @@ public class PageStore implements CacheWriter {
*/ */
public int allocatePage() throws SQLException { public int allocatePage() throws SQLException {
if (freePageCount == 0) { if (freePageCount == 0) {
if (freeListRootPageId != 0) {
throw Message.getInternalError("freeListRootPageId:" + freeListRootPageId);
}
if (pageCount * pageSize >= fileLength) { if (pageCount * pageSize >= fileLength) {
long newLength = (pageCount + INCREMENT_PAGES) * pageSize; long newLength = (pageCount + INCREMENT_PAGES) * pageSize;
file.setLength(newLength); file.setLength(newLength);
...@@ -249,8 +256,38 @@ public class PageStore implements CacheWriter { ...@@ -249,8 +256,38 @@ public class PageStore implements CacheWriter {
} }
return pageCount++; return pageCount++;
} }
int todoReturnAFreePage; if (freeListRootPageId == 0) {
return 0; throw Message.getInternalError();
}
PageFreeList free = (PageFreeList) cache.find(freeListRootPageId);
if (free == null) {
free = new PageFreeList(this, freeListRootPageId, 0);
free.read();
}
int id = free.allocate();
freePageCount--;
return id;
}
/**
* Add a page to the free list.
*
* @param pageId the page id
*/
public void freePage(int pageId) throws SQLException {
freePageCount++;
PageFreeList free;
cache.remove(pageId);
if (freeListRootPageId == 0) {
setFreeListRootPage(pageId, false, 0);
} else {
free = (PageFreeList) cache.find(freeListRootPageId);
if (free == null) {
free = new PageFreeList(this, freeListRootPageId, 0);
free.read();
}
free.free(pageId);
}
} }
/** /**
...@@ -325,15 +362,6 @@ public class PageStore implements CacheWriter { ...@@ -325,15 +362,6 @@ public class PageStore implements CacheWriter {
file.write(data.getBytes(), 0, pageSize); file.write(data.getBytes(), 0, pageSize);
} }
/**
* Add a page to the free list.
*
* @param pageId the page id
*/
public void freePage(int pageId) {
int todo;
}
/** /**
* Remove a page from the cache. * Remove a page from the cache.
* *
...@@ -343,4 +371,12 @@ public class PageStore implements CacheWriter { ...@@ -343,4 +371,12 @@ public class PageStore implements CacheWriter {
cache.remove(pageId); cache.remove(pageId);
} }
void setFreeListRootPage(int pageId, boolean existing, int next) throws SQLException {
this.freeListRootPageId = pageId;
if (!existing) {
PageFreeList free = new PageFreeList(this, pageId, next);
updateRecord(free);
}
}
} }
...@@ -278,6 +278,7 @@ java org.h2.test.TestAll timer ...@@ -278,6 +278,7 @@ java org.h2.test.TestAll timer
System.setProperty("h2.check2", "true"); System.setProperty("h2.check2", "true");
/* /*
checksum: no need to checksum all data; every 128th byte is enough; but need position+counter
JCR: for each node type, create a table; one 'dynamic' table with parameter; JCR: for each node type, create a table; one 'dynamic' table with parameter;
option to cache the results option to cache the results
<link rel="icon" type="image/png" href="/path/image.png"> <link rel="icon" type="image/png" href="/path/image.png">
......
...@@ -13,6 +13,7 @@ import java.io.FileInputStream; ...@@ -13,6 +13,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.sql.SQLException;
import java.util.Random; import java.util.Random;
import org.h2.engine.ConnectionInfo; import org.h2.engine.ConnectionInfo;
import org.h2.engine.Database; import org.h2.engine.Database;
...@@ -21,11 +22,12 @@ import org.h2.store.PageInputStream; ...@@ -21,11 +22,12 @@ import org.h2.store.PageInputStream;
import org.h2.store.PageOutputStream; import org.h2.store.PageOutputStream;
import org.h2.store.PageStore; import org.h2.store.PageStore;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.IntArray;
/** /**
* Test page store input and output streams. * Test the page store.
*/ */
public class TestPageStoreStreams extends TestBase { public class TestPageStore extends TestBase {
/** /**
* Run just this test. * Run just this test.
...@@ -37,20 +39,56 @@ public class TestPageStoreStreams extends TestBase { ...@@ -37,20 +39,56 @@ public class TestPageStoreStreams extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
testFuzz(); testAllocateFree();
testPerformance(false, 1000); testStreamFuzz();
testStreamPerformance(false, 1000);
// testPerformance(true, 1000000); // testPerformance(true, 1000000);
// testPerformance(false, 1000000); // testPerformance(false, 1000000);
} }
private void testPerformance(boolean file, int count) throws Exception { private void testAllocateFree() throws SQLException {
String name = "mem:pageStoreStreams"; String fileName = getTestDir("/pageStore");
new File(fileName).delete();
File f = new File(fileName + ".dat");
f.delete();
Database db = getDatabase();
PageStore store = new PageStore(db, fileName, "rw", 8192);
store.setPageSize(1024);
store.open();
IntArray list = new IntArray();
int test;
int size = 270;
for (int i = 0; i < size; i++) {
int id = store.allocatePage();
list.add(id);
}
for (int i = 0; i < size; i++) {
int id = list.get(i);
store.freePage(id);
}
for (int i = 0; i < size; i++) {
int id = store.allocatePage();
int expected = list.get(list.size() - 1 - i);
assertEquals(expected, id);
}
store.close();
db.shutdownImmediately();
new File(fileName).delete();
f.delete();
}
private Database getDatabase() throws SQLException {
String name = "mem:pageStore";
ConnectionInfo ci = new ConnectionInfo(name); ConnectionInfo ci = new ConnectionInfo(name);
Database db = new Database(name, ci, null); return new Database(name, ci, null);
String fileName = getTestDir("/pageStoreStreams"); }
private void testStreamPerformance(boolean file, int count) throws Exception {
String fileName = getTestDir("/pageStore");
new File(fileName).delete(); new File(fileName).delete();
File f = new File(fileName + ".dat"); File f = new File(fileName + ".dat");
f.delete(); f.delete();
Database db = getDatabase();
PageStore store = new PageStore(db, fileName, "rw", 8192); PageStore store = new PageStore(db, fileName, "rw", 8192);
store.setPageSize(8 * 1024); store.setPageSize(8 * 1024);
byte[] buff = new byte[100]; byte[] buff = new byte[100];
...@@ -88,7 +126,7 @@ public class TestPageStoreStreams extends TestBase { ...@@ -88,7 +126,7 @@ public class TestPageStoreStreams extends TestBase {
f.delete(); f.delete();
} }
private void testFuzz() throws Exception { private void testStreamFuzz() throws Exception {
String name = "mem:pageStoreStreams"; String name = "mem:pageStoreStreams";
ConnectionInfo ci = new ConnectionInfo(name); ConnectionInfo ci = new ConnectionInfo(name);
Database db = new Database(name, ci, null); Database db = new Database(name, ci, null);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论