It is possible to reproducibly corrupt a H2 DB on MVStore (single process, single thread) when killing the process while it is executing a statement.
This appears to only happen if the statement's result set spills to disk (from the stack, it looks like spilling is now done within the MVStore itself ?)
Reproduction code:
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class H2Test {
public static void main(String[] args) throws Exception {
Class.forName("org.h2.Driver");
final String SHORT_STRING = "a quite short string";
final Connection c = DriverManager.getConnection("jdbc:h2:/tmp/stuff");
System.out.println("Creating DB");
try (Statement st = c.createStatement()) {
st.execute("create table stuff (a varchar)");
for (int i = 0; i < 40001; i++) {
st.execute("insert into stuff values ('" + SHORT_STRING + "')");
}
}
c.commit();
System.out.println("DB created");
while (true) {
try (Statement st = c.createStatement()) {
long before = System.currentTimeMillis();
System.out.println("Start query");
st.executeQuery("select * from stuff where a = '" + SHORT_STRING + "' order by a desc");
System.out.println("Query done in " + (System.currentTimeMillis() - before));
}
}
}
}
Compile and run with java -cp h2-1.4.196.jar:. -Xmx1g test.H2Test
The Xmx1g + 40001 records + order by ensure that the result set spills
While the program is running, kill it abruptly (kill -9)
Then try to reopen /tmp/stuff:
Exception in thread "main" org.h2.jdbc.JdbcSQLException: General error: "java.lang.NullPointerException" [50000-196]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:168)
at org.h2.message.DbException.convert(DbException.java:295)
at org.h2.engine.Database.openDatabase(Database.java:307)
at org.h2.engine.Database.<init>(Database.java:270)
at org.h2.engine.Engine.openSession(Engine.java:64)
at org.h2.engine.Engine.openSession(Engine.java:176)
at org.h2.engine.Engine.createSessionAndValidate(Engine.java:154)
at org.h2.engine.Engine.createSession(Engine.java:137)
at org.h2.engine.Engine.createSession(Engine.java:27)
at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:354)
at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:116)
at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:100)
at org.h2.Driver.connect(Driver.java:69)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:247)
at org.h2.tools.Shell.runTool(Shell.java:148)
at org.h2.tools.Shell.main(Shell.java:81)
Caused by: java.lang.NullPointerException
at org.h2.mvstore.db.ValueDataType.compare(ValueDataType.java:104)
at org.h2.mvstore.MVMap.compare(MVMap.java:713)
at org.h2.mvstore.Page.binarySearch(Page.java:334)
at org.h2.mvstore.MVMap.binarySearch(MVMap.java:466)
at org.h2.mvstore.MVMap.get(MVMap.java:455)
at org.h2.mvstore.db.TransactionStore.commit(TransactionStore.java:349)
at org.h2.mvstore.db.TransactionStore$Transaction.commit(TransactionStore.java:783)
at org.h2.mvstore.db.MVTableEngine$Store.initTransactions(MVTableEngine.java:254)
at org.h2.engine.Database.open(Database.java:767)
at org.h2.engine.Database.openDatabase(Database.java:276)
... 14 more
The corruption is reproducible around 80% of times
H2 1.4.196
It is possible to reproducibly corrupt a H2 DB on MVStore (single process, single thread) when killing the process while it is executing a statement. This appears to only happen if the statement's result set spills to disk (from the stack, it looks like spilling is now done within the MVStore itself ?)
Reproduction code:
Compile and run with
java -cp h2-1.4.196.jar:. -Xmx1g test.H2Test
The Xmx1g + 40001 records + order by ensure that the result set spills
While the program is running, kill it abruptly (kill -9)
Then try to reopen /tmp/stuff:
The corruption is reproducible around 80% of times