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

New experimental page store.

上级 a18e7177
......@@ -15,58 +15,56 @@ import org.h2.util.IntArray;
/**
* The list of free pages of a page store.
* 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>5-8: the next page (if there are more) or number of entries
* </li><li>9-remainder: data (4 bytes each entry)
* </li></ul>
*/
public class PageFreeList {
public class PageFreeList extends Record {
private final PageStore store;
private DataPage page;
private int pageId;
private final DataPage page;
private final IntArray array = new IntArray();
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;
readTrunk(pageId);
int maybeWorkLikeAStack;
int alsoReturnTrunkPagesOnceTheyAreEmpty;
this.nextPage = nextPage;
}
int allocate() throws SQLException {
while (true) {
int size = array.size();
if (size > 0) {
int x = array.get(size - 1);
array.remove(size - 1);
return x;
}
if (nextPage == 0) {
return -1;
}
readTrunk(nextPage);
}
store.updateRecord(this);
// no more free pages in this list:
// set the next page (may be 0, meaning no free pages)
store.setFreeListRootPage(nextPage, true, 0);
// and then return the page itself
return getPos();
}
private void readTrunk(int pageId) throws SQLException {
if (nextPage == 0) {
return;
private int getMaxSize() {
return (store.getPageSize() - 9) / DataPage.LENGTH_INT;
}
int parentPage = pageId;
pageId = nextPage;
store.readPage(pageId, page);
void read() throws SQLException {
store.readPage(getPos(), page);
int p = page.readInt();
int t = page.readByte();
boolean last = (t & Page.FLAG_LAST) != 0;
t &= ~Page.FLAG_LAST;
if (t != Page.TYPE_FREE_LIST || p != parentPage) {
if (t != Page.TYPE_FREE_LIST || p != 0) {
throw Message.getSQLException(
ErrorCode.FILE_CORRUPTED_1,
"type:" + t + " parent:" + p +
" expected type:" + Page.TYPE_FREE_LIST + " expected parent:" + parentPage);
" expected type:" + Page.TYPE_FREE_LIST);
}
int size;
if (last) {
......@@ -74,15 +72,46 @@ public class PageFreeList {
size = page.readInt();
} else {
nextPage = page.readInt();
size = (store.getPageSize() - page.length()) / DataPage.LENGTH_INT;
size = getMaxSize();
}
for (int i = 0; i < size; i++) {
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 {
private int systemRootPageId;
private int freeListRootPageId;
private int freePageCount;
/**
* Number of pages (including free pages).
*/
private int pageCount;
private int writeCount;
private long fileLength;
......@@ -242,6 +246,9 @@ public class PageStore implements CacheWriter {
*/
public int allocatePage() throws SQLException {
if (freePageCount == 0) {
if (freeListRootPageId != 0) {
throw Message.getInternalError("freeListRootPageId:" + freeListRootPageId);
}
if (pageCount * pageSize >= fileLength) {
long newLength = (pageCount + INCREMENT_PAGES) * pageSize;
file.setLength(newLength);
......@@ -249,8 +256,38 @@ public class PageStore implements CacheWriter {
}
return pageCount++;
}
int todoReturnAFreePage;
return 0;
if (freeListRootPageId == 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 {
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.
*
......@@ -343,4 +371,12 @@ public class PageStore implements CacheWriter {
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
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;
option to cache the results
<link rel="icon" type="image/png" href="/path/image.png">
......
......@@ -13,6 +13,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
import java.util.Random;
import org.h2.engine.ConnectionInfo;
import org.h2.engine.Database;
......@@ -21,11 +22,12 @@ import org.h2.store.PageInputStream;
import org.h2.store.PageOutputStream;
import org.h2.store.PageStore;
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.
......@@ -37,20 +39,56 @@ public class TestPageStoreStreams extends TestBase {
}
public void test() throws Exception {
testFuzz();
testPerformance(false, 1000);
testAllocateFree();
testStreamFuzz();
testStreamPerformance(false, 1000);
// testPerformance(true, 1000000);
// testPerformance(false, 1000000);
}
private void testPerformance(boolean file, int count) throws Exception {
String name = "mem:pageStoreStreams";
private void testAllocateFree() throws SQLException {
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);
Database db = new Database(name, ci, null);
String fileName = getTestDir("/pageStoreStreams");
return new Database(name, ci, null);
}
private void testStreamPerformance(boolean file, int count) throws Exception {
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(8 * 1024);
byte[] buff = new byte[100];
......@@ -88,7 +126,7 @@ public class TestPageStoreStreams extends TestBase {
f.delete();
}
private void testFuzz() throws Exception {
private void testStreamFuzz() throws Exception {
String name = "mem:pageStoreStreams";
ConnectionInfo ci = new ConnectionInfo(name);
Database db = new Database(name, ci, null);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论