Hi Noel,
I did have some time today and I managed to write a test case (for 1.3.174)
:-)
When running the application you should get:
General error: "java.lang.RuntimeException: Undo entry not written"; SQL
statement:
UPDATE values_7 SET timeend = 21332841064754 WHERE timeend =
9223372036854775807 [50000-174]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:332)
at org.h2.message.DbException.get(DbException.java:161)
at org.h2.message.DbException.convert(DbException.java:284)
at org.h2.table.RegularTable.removeRow(RegularTable.java:394)
at org.h2.table.Table.updateRows(Table.java:449)
at org.h2.command.dml.Update.update(Update.java:144)
at org.h2.command.CommandContainer.update(CommandContainer.java:79)
at org.h2.command.Command.executeUpdate(Command.java:253)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:181)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:156)
at
com.gematronik.analyst.db.test.H2MultiThreadTest1$Inserter.run(H2MultiThreadTest1.java:121)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.RuntimeException: Undo entry not written
at org.h2.message.DbException.throwInternalError(DbException.java:231)
at org.h2.store.PageLog.addUndo(PageLog.java:485)
at org.h2.store.PageStore.update(PageStore.java:1061)
at org.h2.index.PageBtreeLeaf.remove(PageBtreeLeaf.java:239)
at org.h2.index.PageBtreeNode.remove(PageBtreeNode.java:332)
at org.h2.index.PageBtreeNode.remove(PageBtreeNode.java:332)
at org.h2.index.PageBtreeIndex.remove(PageBtreeIndex.java:239)
at org.h2.table.RegularTable.removeRow(RegularTable.java:376)
... 8 more
If the problem does not occur after 10 minutes just remove the database
file and restart the application. Usually I get the error around each 3
attempts...
The test case seems to be quite similar to the problem described here:
https://groups.google.com/forum/#!searchin/h2-database/Undo$20entry$20not$20written/h2-database/E_2gUWb-iGc/rkbaekWMUw4J
I'll be off until next year but I hope the test case will give you some
more hints!
Uli
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/groups/opt_out.
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import org.h2.jdbcx.JdbcConnectionPool;
public class H2MultiThreadTest1
{
private Connection conn;
private final JdbcConnectionPool pool;
private int maxId = 20;
public H2MultiThreadTest1()
throws ClassNotFoundException, SQLException
{
System.out.println("Open connections...");
Class.forName("org.h2.Driver");
this.pool = JdbcConnectionPool.create("jdbc:h2:~/h2/test/db1;LOCK_TIMEOUT=86400000;MULTI_THREADED=1", "tester",
"test");
this.pool.setMaxConnections(Integer.MAX_VALUE);
// get one connection to keep the db open
this.conn = this.pool.getConnection();
}
public Connection getConnection()
throws SQLException
{
Connection con = this.pool.getConnection();
con.setAutoCommit(false);
return con;
}
private static String[] BASE_TABLE_CREATION =
{ "DROP TABLE IF EXISTS currvalues",
"CREATE TABLE currvalues (id INTEGER NOT NULL PRIMARY KEY, value VARCHAR NOT NULL, time BIGINT NOT NULL)" };
private static String[] ID_CREATION =
{
"DROP TABLE IF EXISTS values_%id%",
"CREATE TABLE values_%id% (time BIGINT NOT NULL, timeend BIGINT NOT NULL DEFAULT 9223372036854775807,value VARCHAR NOT NULL, PRIMARY KEY (time))",
"CREATE UNIQUE INDEX IF NOT EXISTS i_values_%id% on values_%id% (timeend)", };
public void createBaseTables()
throws SQLException
{
Statement stmt = this.conn.createStatement();
for (String sql : BASE_TABLE_CREATION)
{
stmt.execute(sql);
}
for (int id = 0; id < this.maxId; id++)
{
for (String sql : ID_CREATION)
{
sql = sql.replace("%id%", String.valueOf(id));
stmt.execute(sql);
}
stmt.execute("INSERT INTO currvalues VALUES(" + id + ",'0'," + System.nanoTime() + ")");
}
this.conn.commit();
stmt.close();
}
public void runTest()
{
System.out.println("Starting test...");
new Thread(new Inserter(), "Inserter").start();
new Thread(new Deletor(), "Deletor").start();
for (int i = 0; i < 2; i++)
{
new Thread(new Selector(), "Query_" + i).start();
}
new Thread(new Selector2(), "Sel2").start();
try
{
Thread.sleep(5000);
}
catch (InterruptedException exp)
{
System.err.println(exp);
}
}
private class Inserter
implements Runnable
{
@Override
public void run()
{
ThreadLocalRandom r = ThreadLocalRandom.current();
try
{
long value = 0;
boolean cont = true;
Connection con = getConnection();
while (cont)
{
int id = r.nextInt(H2MultiThreadTest1.this.maxId);
value++;
Statement stmt = con.createStatement();
stmt.execute("SELECT * from currvalues where 1=2 FOR UPDATE");
stmt.execute("SELECT * from values_" + id + " where 1=2 FOR UPDATE");
long time = System.nanoTime();
stmt.execute("UPDATE values_" + id + " SET timeend = " + time
+ " WHERE timeend = 9223372036854775807");
stmt.execute("UPDATE currvalues SET value='" + value + "', time = " + time + " WHERE id = " + id);
stmt.execute("INSERT INTO values_" + id + " (time, value) VALUES (" + time + ", '" + value + "')");
con.commit();
stmt.close();
}
con.close();
}
catch (SQLException exp)
{
System.err.println(exp);
exp.printStackTrace();
}
}
}
private class Deletor
implements Runnable
{
@Override
public void run()
{
try
{
while (true)
{
Connection con = getConnection();
Statement stmt = con.createStatement();
long time = System.nanoTime() - TimeUnit.MINUTES.toNanos(5);
for (int id = 0; id < H2MultiThreadTest1.this.maxId; id++)
{
stmt.execute("delete from values_" + id + " where timeend < " + time);
}
con.commit();
stmt.close();
con.close();
try
{
Thread.sleep(TimeUnit.MINUTES.toMillis(3));
}
catch (InterruptedException exp)
{
}
}
}
catch (SQLException exp)
{
System.err.println(exp);
exp.printStackTrace();
}
}
}
private class Selector
implements Runnable
{
@Override
public void run()
{
try
{
while (true)
{
Connection con = getConnection();
Statement stmt = con.createStatement();
ThreadLocalRandom r = ThreadLocalRandom.current();
long timeStart = System.nanoTime() - r.nextInt(1, 300) * TimeUnit.SECONDS.toNanos(1);
long timeEnd = Math.min(System.nanoTime(),
timeStart + r.nextInt(1, 300) * TimeUnit.SECONDS.toNanos(1));
long id = r.nextLong(H2MultiThreadTest1.this.maxId);
ResultSet rs = stmt.executeQuery("SELECT time \"time\", value \"value\" FROM values_" + id
+ " WHERE time <= " + timeEnd + " AND timeend >= " + timeStart
+ " ORDER BY \"time\" LIMIT 500000");
while (rs.next())
{
long t = rs.getLong(1);
String v = rs.getString(2);
}
rs.close();
stmt.close();
con.close();
try
{
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
}
catch (InterruptedException exp)
{
}
}
}
catch (SQLException exp)
{
System.err.println(exp);
exp.printStackTrace();
}
}
}
private class Selector2
implements Runnable
{
@Override
public void run()
{
try
{
while (true)
{
Connection con = getConnection();
Statement stmt = con.createStatement();
ThreadLocalRandom r = ThreadLocalRandom.current();
long timeStart = System.nanoTime() - r.nextInt(1, 300) * TimeUnit.SECONDS.toNanos(1);
long timeEnd = Math.min(System.nanoTime(),
timeStart + r.nextInt(1, 300) * TimeUnit.SECONDS.toNanos(1));
long id1 = r.nextLong(H2MultiThreadTest1.this.maxId);
long id2 = r.nextLong(H2MultiThreadTest1.this.maxId);
String query = "SELECT %timefrom% \"time\", ifnull(v1.value, v.c1) \"%label1%\", ifnull(v2.value, v.c1) \"%label2%\" "
+ "FROM values('-nc-') v "
+ "LEFT OUTER JOIN ( "
+ " SELECT vi1.value "
+ " FROM values_%id1% vi1 "
+ " WHERE vi1.time = (SELECT min(time) FROM values_%id1% WHERE timeend > %timefrom%) AND vi1.time < %timefrom% "
+ ") v1 ON 1=1 "
+ "LEFT OUTER JOIN ( "
+ " SELECT vi2.value "
+ " FROM values_%id2% vi2 "
+ " WHERE vi2.time = (SELECT min(time) FROM values_%id2% WHERE timeend > %timefrom%) AND vi2.time < %timefrom% "
+ ") v2 ON 1=1 " + //
"UNION " + //
"SELECT times.time, v1.value \"%label1%\", v2.value \"%label2%\" " + //
"FROM " + //
" ( " + //
" SELECT DISTINCT v.time " + //
" FROM values_%id1% v " + //
" WHERE v.time BETWEEN %timefrom% and %timeto% " + //
" UNION " + //
" SELECT DISTINCT v.time " + //
" FROM values_%id2% v " + //
" WHERE v.time BETWEEN %timefrom% and %timeto% " + //
" ) AS times " + //
"LEFT OUTER JOIN " + //
" values_%id1% v1 " + //
" ON times.time=v1.time AND v1.time BETWEEN %timefrom% AND %timeto% " + //
"LEFT OUTER JOIN " + //
" values_%id2% v2 " + //
" ON times.time=v2.time AND v2.time BETWEEN %timefrom% AND %timeto% " + //
"ORDER BY \"time\" " + //
"LIMIT 500000 "; //
query = query.replaceAll("%timefrom%", String.valueOf(timeStart))
.replaceAll("%timeto%", String.valueOf(timeEnd)).replaceAll("%id1%", String.valueOf(id1))
.replaceAll("%id2%", String.valueOf(id2)).replaceAll("%label1%", "label1")
.replaceAll("%label2%", "label2");
ResultSet rs = stmt.executeQuery(query);
while (rs.next())
{
long t = rs.getLong(1);
String v = rs.getString(2);
String v2 = rs.getString(3);
}
rs.close();
stmt.close();
con.close();
try
{
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
}
catch (InterruptedException exp)
{
}
}
}
catch (SQLException exp)
{
System.err.println(exp);
exp.printStackTrace();
}
}
}
public static void main(String[] args)
throws SQLException, ClassNotFoundException
{
H2MultiThreadTest1 test1 = new H2MultiThreadTest1();
test1.createBaseTables();
test1.runTest();
}
}