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:
parent
59e096e2ea
commit
939337b2db
@ -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()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user