3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 23:32:16 +00:00

Fix premature optimization in H2FullPrunedBlockStore (resets state)

This replaces the H2FullPrunedBlockStore schema with one that will
take (maybe) some more disk space while being a good bit faster.
The tradeoff made was really not worth it.

Also adds a version field to settings database incase something
other than schema changes in the future.
This commit is contained in:
Matt Corallo 2013-03-29 16:08:03 -04:00
parent 59e096e2ea
commit 939337b2db

View File

@ -58,6 +58,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
+ ")"; + ")";
static final String CHAIN_HEAD_SETTING = "chainhead"; static final String CHAIN_HEAD_SETTING = "chainhead";
static final String VERIFIED_CHAIN_HEAD_SETTING = "verifiedchainhead"; static final String VERIFIED_CHAIN_HEAD_SETTING = "verifiedchainhead";
static final String VERSION_SETTING = "version";
static final String CREATE_HEADERS_TABLE = "CREATE TABLE headers ( " static final String CREATE_HEADERS_TABLE = "CREATE TABLE headers ( "
+ "hash BINARY(28) NOT NULL CONSTRAINT headers_pk PRIMARY KEY," + "hash BINARY(28) NOT NULL CONSTRAINT headers_pk PRIMARY KEY,"
@ -75,18 +76,13 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
+ ")"; + ")";
static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX heightIndex ON undoableBlocks (height)"; static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX heightIndex ON undoableBlocks (height)";
static final String CREATE_OPEN_OUTPUT_INDEX_TABLE = "CREATE TABLE openOutputsIndex ("
+ "hash BINARY(32) NOT NULL CONSTRAINT openOutputsIndex_pk PRIMARY KEY,"
+ "height INT NOT NULL,"
+ "id BIGINT NOT NULL AUTO_INCREMENT"
+ ")";
static final String CREATE_OPEN_OUTPUT_TABLE = "CREATE TABLE openOutputs (" static final String CREATE_OPEN_OUTPUT_TABLE = "CREATE TABLE openOutputs ("
+ "id BIGINT NOT NULL," + "hash BINARY(32) NOT NULL,"
+ "index INT NOT NULL," + "index INT NOT NULL,"
+ "height INT NOT NULL,"
+ "value BLOB NOT NULL," + "value BLOB NOT NULL,"
+ "scriptBytes BLOB NOT NULL," + "scriptBytes BLOB NOT NULL,"
+ "PRIMARY KEY (id, index)," + "PRIMARY KEY (hash, index),"
+ "CONSTRAINT openOutputs_fk FOREIGN KEY (id) REFERENCES openOutputsIndex(id)"
+ ")"; + ")";
/** /**
@ -179,7 +175,6 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
s.executeUpdate("DROP TABLE headers"); s.executeUpdate("DROP TABLE headers");
s.executeUpdate("DROP TABLE undoableBlocks"); s.executeUpdate("DROP TABLE undoableBlocks");
s.executeUpdate("DROP TABLE openOutputs"); s.executeUpdate("DROP TABLE openOutputs");
s.executeUpdate("DROP TABLE openOutputsIndex");
s.close(); s.close();
createTables(); createTables();
initFromDatabase(); initFromDatabase();
@ -202,21 +197,24 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
log.debug("H2FullPrunedBlockStore : CREATE undoable block index"); log.debug("H2FullPrunedBlockStore : CREATE undoable block index");
s.executeUpdate(CREATE_UNDOABLE_TABLE_INDEX); s.executeUpdate(CREATE_UNDOABLE_TABLE_INDEX);
log.debug("H2FullPrunedBlockStore : CREATE open output index table");
s.executeUpdate(CREATE_OPEN_OUTPUT_INDEX_TABLE);
log.debug("H2FullPrunedBlockStore : CREATE open output table"); log.debug("H2FullPrunedBlockStore : CREATE open output table");
s.executeUpdate(CREATE_OPEN_OUTPUT_TABLE); s.executeUpdate(CREATE_OPEN_OUTPUT_TABLE);
s.executeUpdate("INSERT INTO settings(name, value) VALUES('" + CHAIN_HEAD_SETTING + "', NULL)"); s.executeUpdate("INSERT INTO settings(name, value) VALUES('" + CHAIN_HEAD_SETTING + "', NULL)");
s.executeUpdate("INSERT INTO settings(name, value) VALUES('" + VERIFIED_CHAIN_HEAD_SETTING + "', NULL)"); s.executeUpdate("INSERT INTO settings(name, value) VALUES('" + VERIFIED_CHAIN_HEAD_SETTING + "', NULL)");
s.executeUpdate("INSERT INTO settings(name, value) VALUES('" + VERSION_SETTING + "', '03')");
s.close(); s.close();
createNewStore(params); createNewStore(params);
} }
private void initFromDatabase() throws SQLException, BlockStoreException { private void initFromDatabase() throws SQLException, BlockStoreException {
Statement s = conn.get().createStatement(); Statement s = conn.get().createStatement();
ResultSet rs = s.executeQuery("SELECT value FROM settings WHERE name = '" + CHAIN_HEAD_SETTING + "'"); ResultSet rs = s.executeQuery("SHOW TABLES");
while (rs.next())
if (rs.getString(1).equalsIgnoreCase("openOutputsIndex"))
throw new BlockStoreException("Attempted to open a H2 database with an old schema, please reset database.");
rs = s.executeQuery("SELECT value FROM settings WHERE name = '" + CHAIN_HEAD_SETTING + "'");
if (!rs.next()) { if (!rs.next()) {
throw new BlockStoreException("corrupt H2 block store - no chain head pointer"); throw new BlockStoreException("corrupt H2 block store - no chain head pointer");
} }
@ -323,23 +321,13 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
rs.close(); rs.close();
System.out.printf("Undoable Blocks size: %d, count: %d, average size: %f%n", size, count, (double)size/count); System.out.printf("Undoable Blocks size: %d, count: %d, average size: %f%n", size, count, (double)size/count);
totalSize += size; size = 0; count = 0;
rs = s.executeQuery("SELECT id FROM openOutputsIndex");
while (rs.next()) {
size += 32; // hash
size += 4; // height
size += 8; // id
count++;
}
rs.close();
System.out.printf("Open Outputs Index size: %d, count: %d, size in id indexes: %d%n", size, count, count * 8);
totalSize += size; size = 0; count = 0; totalSize += size; size = 0; count = 0;
long scriptSize = 0; long scriptSize = 0;
rs = s.executeQuery("SELECT value, scriptBytes FROM openOutputs"); rs = s.executeQuery("SELECT value, scriptBytes FROM openOutputs");
while (rs.next()) { while (rs.next()) {
size += 8; // id size += 32; // hash
size += 4; // index size += 4; // index
size += 4; // height
size += rs.getBytes(1).length; size += rs.getBytes(1).length;
size += rs.getBytes(2).length; size += rs.getBytes(2).length;
scriptSize += rs.getBytes(2).length; scriptSize += rs.getBytes(2).length;
@ -646,9 +634,8 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
PreparedStatement s = null; PreparedStatement s = null;
try { try {
s = conn.get() s = conn.get()
.prepareStatement("SELECT openOutputsIndex.height, openOutputs.value, openOutputs.scriptBytes " + .prepareStatement("SELECT height, value, scriptBytes FROM openOutputs " +
"FROM openOutputsIndex NATURAL JOIN openOutputs " + "WHERE hash = ? AND index = ?");
"WHERE openOutputsIndex.hash = ? AND openOutputs.index = ?");
s.setBytes(1, hash.getBytes()); s.setBytes(1, hash.getBytes());
// index is actually an unsigned int // index is actually an unsigned int
s.setInt(2, (int)index); s.setInt(2, (int)index);
@ -676,28 +663,14 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
maybeConnect(); maybeConnect();
PreparedStatement s = null; PreparedStatement s = null;
try { try {
try { s = conn.get().prepareStatement("INSERT INTO openOutputs (hash, index, height, value, scriptBytes) " +
s = conn.get().prepareStatement("INSERT INTO openOutputsIndex(hash, height)" "VALUES (?, ?, ?, ?, ?)");
+ " VALUES(?, ?)");
s.setBytes(1, out.getHash().getBytes());
s.setInt(2, out.getHeight());
s.executeUpdate();
} catch (SQLException e) {
if (e.getErrorCode() != 23505)
throw e;
} finally {
if (s != null)
s.close();
}
s = conn.get().prepareStatement("INSERT INTO openOutputs (id, index, value, scriptBytes) " +
"VALUES ((SELECT id FROM openOutputsIndex WHERE hash = ?), " +
"?, ?, ?)");
s.setBytes(1, out.getHash().getBytes()); s.setBytes(1, out.getHash().getBytes());
// index is actually an unsigned int // index is actually an unsigned int
s.setInt(2, (int)out.getIndex()); s.setInt(2, (int)out.getIndex());
s.setBytes(3, out.getValue().toByteArray()); s.setInt(3, out.getHeight());
s.setBytes(4, out.getScriptBytes()); s.setBytes(4, out.getValue().toByteArray());
s.setBytes(5, out.getScriptBytes());
s.executeUpdate(); s.executeUpdate();
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
@ -718,22 +691,12 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
throw new BlockStoreException("Tried to remove a StoredTransactionOutput from H2FullPrunedBlockStore that it didn't have!"); throw new BlockStoreException("Tried to remove a StoredTransactionOutput from H2FullPrunedBlockStore that it didn't have!");
try { try {
PreparedStatement s = conn.get() PreparedStatement s = conn.get()
.prepareStatement("DELETE FROM openOutputs " + .prepareStatement("DELETE FROM openOutputs WHERE hash = ? AND index = ?");
"WHERE id = (SELECT id FROM openOutputsIndex WHERE hash = ?) AND index = ?");
s.setBytes(1, out.getHash().getBytes()); s.setBytes(1, out.getHash().getBytes());
// index is actually an unsigned int // index is actually an unsigned int
s.setInt(2, (int)out.getIndex()); s.setInt(2, (int)out.getIndex());
s.executeUpdate(); s.executeUpdate();
s.close(); s.close();
// This is quite an ugly query, is there no better way?
s = conn.get().prepareStatement("DELETE FROM openOutputsIndex " +
"WHERE hash = ? AND 1 = (CASE WHEN ((SELECT COUNT(*) FROM openOutputs WHERE id =" +
"(SELECT id FROM openOutputsIndex WHERE hash = ?)) = 0) THEN 1 ELSE 0 END)");
s.setBytes(1, out.getHash().getBytes());
s.setBytes(2, out.getHash().getBytes());
s.executeUpdate();
s.close();
} catch (SQLException e) { } catch (SQLException e) {
throw new BlockStoreException(e); throw new BlockStoreException(e);
} }
@ -773,8 +736,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
PreparedStatement s = null; PreparedStatement s = null;
try { try {
s = conn.get() s = conn.get()
.prepareStatement("SELECT COUNT(*) FROM openOutputsIndex " + .prepareStatement("SELECT COUNT(*) FROM openOutputs WHERE hash = ?");
"WHERE hash = ?");
s.setBytes(1, hash.getBytes()); s.setBytes(1, hash.getBytes());
ResultSet results = s.executeQuery(); ResultSet results = s.executeQuery();
if (!results.next()) { if (!results.next()) {