/* * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.test.mvcc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import org.h2.api.ErrorCode; import org.h2.jdbc.JdbcSQLException; import org.h2.test.TestBase; import org.h2.util.IOUtils; /** * Additional MVCC (multi version concurrency) test cases. */ public class TestMvccMultiThreaded2 extends TestBase { private static final String URL = ";MVCC=TRUE;LOCK_TIMEOUT=120000;MULTI_THREADED=TRUE"; /** * Run just this test. * * @param a ignored */ public static void main(String... a) throws Exception { TestBase test = TestBase.createCaller().init(); test.config.mvcc = true; test.config.lockTimeout = 120000; test.config.memory = true; test.config.multiThreaded = true; test.test(); } @Override public void test() throws SQLException, InterruptedException { testSelectForUpdateConcurrency(); } private void testSelectForUpdateConcurrency() throws SQLException, InterruptedException { deleteDb(getTestName()); Connection conn = getConnection(getTestName() + URL); conn.setAutoCommit(false); String sql = "CREATE TABLE test (" + "entity_id INTEGER NOT NULL PRIMARY KEY, " + "lastUpdated INTEGER NOT NULL)"; Statement smtm = conn.createStatement(); smtm.executeUpdate(sql); PreparedStatement ps = conn.prepareStatement( "INSERT INTO test (entity_id, lastUpdated) VALUES (?, ?)"); ps.setInt(1, 1); ps.setInt(2, 100); ps.executeUpdate(); conn.commit(); ArrayList<SelectForUpdate> threads = new ArrayList<SelectForUpdate>(); for (int i = 0; i < 100; i++) { SelectForUpdate sfu = new SelectForUpdate(); threads.add(sfu); sfu.start(); } for (SelectForUpdate sfu : threads) { sfu.join(); } IOUtils.closeSilently(conn); deleteDb(getTestName()); } private class SelectForUpdate extends Thread { @Override public void run() { final long start = System.currentTimeMillis(); boolean done = false; Connection conn = null; try { conn = getConnection(getTestName() + URL); conn.setAutoCommit(false); while (!done) { try { PreparedStatement ps = conn.prepareStatement( "SELECT * FROM test WHERE entity_id = ? FOR UPDATE"); ps.setString(1, "1"); ResultSet rs = ps.executeQuery(); assertTrue(rs.next()); assertTrue(rs.getInt(2) == 100); conn.commit(); long now = System.currentTimeMillis(); if (now - start > 1000 * 60) done = true; } catch (JdbcSQLException e1) { // skip DUPLICATE_KEY_1 to just focus on this bug. if (e1.getErrorCode() != ErrorCode.DUPLICATE_KEY_1) { throw e1; } } } } catch (SQLException e) { TestBase.logError("error", e); } IOUtils.closeSilently(conn); } } }