/** * Copyright (C) 2009-2014 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.test.mt; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.Random; import static org.junit.Assert.fail; public class IdentityInsertMT extends PostgresMTBase { private static final Logger LOG = LoggerFactory.getLogger(IdentityInsertMT.class); private static final int TOTAL_STEPS = 1000; private static final String TABLE_NAME = "t"; private static final String DROP_STMT = "DROP TABLE IF EXISTS " + TABLE_NAME; private static final String CREATE_STMT = "CREATE TABLE " + TABLE_NAME + "(id SERIAL PRIMARY KEY, x INT)"; private static final String INSERT_STMT_FMT = "INSERT INTO " + TABLE_NAME + "(x) VALUES (%d) RETURNING *"; private static final String COMMIT_STMT = "COMMIT"; private static final String ROLLBACK_STMT = "ROLLBACK"; private final long seed = Long.parseLong(System.getProperty("fdbsql.test.seed", ""+System.currentTimeMillis())); private final Random random = new Random(seed); private final List<Connection> connections = new ArrayList<>(); private final List<Statement> statements = new ArrayList<>(); @Rule public final TestWatcher watcher = new TestWatcher() { protected void failed(Throwable e, Description description) { LOG.error("Failure with seed: {}", seed); } }; @Before public void tearDown() { statements.clear(); for(Connection c : connections) { try { c.close(); } catch(SQLException e) { // Ignore } } connections.clear(); } @Test public void oneConn() throws Exception { run(1); } @Test public void twoConn() throws Exception { run(2); } @Test public void tenConn() throws Exception { run(10); } private void run(int connCount) throws Exception { random.setSeed(seed); for(int i = 0; i < connCount; ++i) { Connection c = createConnection(); c.setAutoCommit(false); connections.add(c); statements.add(c.createStatement()); } statements.get(0).execute(DROP_STMT); statements.get(0).execute(CREATE_STMT); for(int i = 0; i < TOTAL_STEPS; ++i) { int sIdx = random.nextInt(statements.size()); Statement s = statements.get(sIdx); int v = random.nextInt(100); String stmt; if(v < 10) { stmt = ROLLBACK_STMT; } else if(v < 30) { stmt = COMMIT_STMT; } else { stmt = String.format(INSERT_STMT_FMT, v); } try { LOG.debug("conn{}: {}", sIdx, stmt); if(s.execute(stmt)) { ResultSet rs = s.getResultSet(); while(rs.next()) { LOG.debug(" Inserted: ({},{})", rs.getInt(1), rs.getInt(2)); } rs.close(); } } catch(SQLException e) { if(!e.getSQLState().startsWith("40")) { fail(String.format("Non-retryable error on step %d: (%s) %s\n", i, e.getSQLState(), e.getMessage())); } else { LOG.debug(" Retryable error: {}", e.getSQLState()); s.getConnection().rollback(); } } } } }