java/yb-pgsql/src/test/java/org/yb/pgsql/TestPgTransactions.java
/**
   * This test runs conflicting transactions trying to insert the same row and verifies that
   * exactly one of them gets committed.
   */
This test runs conflicting transactions trying to insert the same row and verifies that
exactly one of them gets committed.
This test runs conflicting transactions trying to insert the same row and verifies that exactly one of them gets committed .
@Test
  public void testTransactionConflicts() throws Exception {
    createSimpleTable("test", "v");
    final IsolationLevel isolation = IsolationLevel.REPEATABLE_READ;
    Connection connection1 = createConnection(isolation, AutoCommit.DISABLED);
    Statement statement1 = connection1.createStatement();
    Connection connection2 = createConnection(isolation, AutoCommit.DISABLED);
    Statement statement2 = connection2.createStatement();
    int numFirstWinners = 0;
    int numSecondWinners = 0;
    final int totalIterations = SanitizerUtil.nonTsanVsTsan(300, 100);
    for (int i = 1; i <= totalIterations; ++i) {
      LOG.info("Starting iteration: i=" + i);
      if (RandomUtils.nextBoolean()) {
        Connection tmpConnection = connection1;
        connection1 = connection2;
        connection2 = tmpConnection;
        Statement tmpStatement = statement1;
        statement1 = statement2;
        statement2 = tmpStatement;
      }
      executeWithTimeout(statement1,
          String.format("INSERT INTO test(h, r, v) VALUES (%d, %d, %d)", i, i, 100 * i));
      boolean executed2 = false;
      try {
        executeWithTimeout(statement2,
            String.format("INSERT INTO test(h, r, v) VALUES (%d, %d, %d)", i, i, 200 * i));
        executed2 = true;
      } catch (PSQLException ex) {
        LOG.info("Error while inserting on the second connection:" + ex.getMessage());
      }
      TransactionState txnState1BeforeCommit = getPgTxnState(connection1);
      TransactionState txnState2BeforeCommit = getPgTxnState(connection2);
      boolean committed1 = commitAndCatchException(connection1, "first connection");
      TransactionState txnState1AfterCommit = getPgTxnState(connection1);
      boolean committed2 = commitAndCatchException(connection2, "second connection");
      TransactionState txnState2AfterCommit = getPgTxnState(connection2);
      LOG.info("i=" + i +
          " executed2=" + executed2 +
          " committed1=" + committed1 +
          " committed2=" + committed2 +
          " txnState1BeforeCommit=" + txnState1BeforeCommit +
          " txnState2BeforeCommit=" + txnState2BeforeCommit +
          " txnState1AfterCommit=" + txnState1AfterCommit +
          " txnState2AfterCommit=" + txnState2AfterCommit +
          " numFirstWinners=" + numFirstWinners +
          " numSecondWinners=" + numSecondWinners);
      assertEquals(TransactionState.IDLE, txnState1AfterCommit);
      assertEquals(TransactionState.IDLE, txnState2AfterCommit);
      if (!committed1 && !committed2) {
        throw new AssertionError("Did not expect both transactions to fail!");
      }
      if (executed2) {
        assertFalse(committed1);
        assertTrue(committed2);
        assertEquals(TransactionState.OPEN, txnState1BeforeCommit);
        assertEquals(TransactionState.OPEN, txnState2BeforeCommit);
        numSecondWinners++;
      } else {
        assertTrue(committed1);
         executed2=false
         committed1=true
         committed2=true
         txnState1BeforeCommit=OPEN
         txnState2BeforeCommit=FAILED
         txnState1AfterCommit=IDLE
         txnState2AfterCommit=IDLE
         TODO: verify if this is consistent with vanilla PostgreSQL behavior.
         assertFalse(committed2);
        assertEquals(TransactionState.OPEN, txnState1BeforeCommit);
        assertEquals(TransactionState.FAILED, txnState2BeforeCommit);
        numFirstWinners++;
      }
    }
    LOG.info(String.format(
        "First txn won in %d cases, second won in %d cases", numFirstWinners, numSecondWinners));
    checkTransactionFairness(numFirstWinners, numSecondWinners, totalIterations, 0.3);
  }
@ Test public void testTransactionConflicts ( ) throws Exception { createSimpleTable ( "test" , "v" ) ; final IsolationLevel isolation = IsolationLevel . REPEATABLE_READ ; Connection connection1 = createConnection ( isolation , AutoCommit . DISABLED ) ; Statement statement1 = connection1 . createStatement ( ) ; Connection connection2 = createConnection ( isolation , AutoCommit . DISABLED ) ; Statement statement2 = connection2 . createStatement ( ) ; int numFirstWinners = 0 ; int numSecondWinners = 0 ; final int totalIterations = SanitizerUtil . nonTsanVsTsan ( 300 , 100 ) ; for ( int i = 1 ; i <= totalIterations ; ++ i ) { LOG . info ( "Starting iteration: i=" + i ) ; if ( RandomUtils . nextBoolean ( ) ) { Connection tmpConnection = connection1 ; connection1 = connection2 ; connection2 = tmpConnection ; Statement tmpStatement = statement1 ; statement1 = statement2 ; statement2 = tmpStatement ; } executeWithTimeout ( statement1 , String . format ( "INSERT INTO test(h, r, v) VALUES (%d, %d, %d)" , i , i , 100 * i ) ) ; boolean executed2 = false ; try { executeWithTimeout ( statement2 , String . format ( "INSERT INTO test(h, r, v) VALUES (%d, %d, %d)" , i , i , 200 * i ) ) ; executed2 = true ; } catch ( PSQLException ex ) { LOG . info ( "Error while inserting on the second connection:" + ex . getMessage ( ) ) ; } TransactionState txnState1BeforeCommit = getPgTxnState ( connection1 ) ; TransactionState txnState2BeforeCommit = getPgTxnState ( connection2 ) ; boolean committed1 = commitAndCatchException ( connection1 , "first connection" ) ; TransactionState txnState1AfterCommit = getPgTxnState ( connection1 ) ; boolean committed2 = commitAndCatchException ( connection2 , "second connection" ) ; TransactionState txnState2AfterCommit = getPgTxnState ( connection2 ) ; LOG . info ( "i=" + i + " executed2=" + executed2 + " committed1=" + committed1 + " committed2=" + committed2 + " txnState1BeforeCommit=" + txnState1BeforeCommit + " txnState2BeforeCommit=" + txnState2BeforeCommit + " txnState1AfterCommit=" + txnState1AfterCommit + " txnState2AfterCommit=" + txnState2AfterCommit + " numFirstWinners=" + numFirstWinners + " numSecondWinners=" + numSecondWinners ) ; assertEquals ( TransactionState . IDLE , txnState1AfterCommit ) ; assertEquals ( TransactionState . IDLE , txnState2AfterCommit ) ; if ( ! committed1 && ! committed2 ) { throw new AssertionError ( "Did not expect both transactions to fail!" ) ; } if ( executed2 ) { assertFalse ( committed1 ) ; assertTrue ( committed2 ) ; assertEquals ( TransactionState . OPEN , txnState1BeforeCommit ) ; assertEquals ( TransactionState . OPEN , txnState2BeforeCommit ) ; numSecondWinners ++ ; } else { assertTrue ( committed1 ) ; assertEquals ( TransactionState . OPEN , txnState1BeforeCommit ) ; assertEquals ( TransactionState . FAILED , txnState2BeforeCommit ) ; numFirstWinners ++ ; } } LOG . info ( String . format ( "First txn won in %d cases, second won in %d cases" , numFirstWinners , numSecondWinners ) ) ; checkTransactionFairness ( numFirstWinners , numSecondWinners , totalIterations , 0.3 ) ; }
This test runs conflicting transactions trying to insert the same row and verifies that
 exactly one of them gets committed.
This test runs conflicting transactions trying to insert the same row and verifies that exactly one of them gets committed .
// Shuffle the two connections between iterations. // TODO: validate the exception message. // Not reporting a stack trace here on purpose, because this will happen a lot in a test. // Whether or not a transaction commits successfully, its state is changed to IDLE after the // commit attempt. // TODO: if this happens, look at why two transactions could fail at the same time. // It looks like in case we get an error on an operation on the second connection, the // commit on that connection succeeds. This makes sense in a way because the client already // knows that the second transaction failed from the original operation failure. BTW the // second transaction is already in a FAILED state before we successfully "commit" it: // // executed2=false // committed1=true // committed2=true // txnState1BeforeCommit=OPEN // txnState2BeforeCommit=FAILED // txnState1AfterCommit=IDLE // txnState2AfterCommit=IDLE // // TODO: verify if this is consistent with vanilla PostgreSQL behavior. // assertFalse(committed2);