From fa6cc52136fc50989f3079e2997a194753c04f82 Mon Sep 17 00:00:00 2001 From: Arjun Narayan Date: Wed, 11 Jan 2017 17:08:28 -0500 Subject: [PATCH] Test App: Add Java code for transaction retries Add example code for transaction retry logic. Compilation and execution instructions are in a comment in the main code file. Fixes #757. --- _includes/app/txn-sample.java | 88 +++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/_includes/app/txn-sample.java b/_includes/app/txn-sample.java index e69de29bb2d..02fb7d3e527 100644 --- a/_includes/app/txn-sample.java +++ b/_includes/app/txn-sample.java @@ -0,0 +1,88 @@ +import java.sql.*; + +/* + + TODO(jseldess): This file needs to be renamed to TxnSample.java to + fit with Java naming conventions (see BasicSample.java). I've held + off on that as that needs renamings on the rest of the doc page. + + You can compile and run this example with a command like: + javac TxnSample.java && java -cp .:~/path/to/postgresql-9.4.1208.jar TxnSample + You can download the postgres JDBC driver jar from https://jdbc.postgresql.org. +*/ + +class InsufficientBalanceException extends Exception {} + +// A simple interface that provides a retryable lambda expression. +interface RetryableTransaction { + public void run(Connection conn) throws Exception; +} + +public class TxnSample { + public static RetryableTransaction transferFunds(int from, int to, int amount) { + return new RetryableTransaction() { + public void run(Connection conn) throws Exception { + ResultSet res = conn.createStatement().executeQuery("SELECT balance FROM accounts WHERE id = " + from); + res.next(); + int balance = res.getInt("balance"); + if(balance < from) { + throw new InsufficientBalanceException(); + } + conn.createStatement().executeUpdate("UPDATE accounts SET balance = balance - " + amount + " where id = " + from); + conn.createStatement().executeUpdate("UPDATE accounts SET balance = balance + " + amount + " where id = " + to); + } + }; + } + + public static void retryTransaction(Connection conn, RetryableTransaction tx) throws Exception { + Savepoint sp = conn.setSavepoint("cockroach_restart"); + while(true) { + try { + // Attempt the transaction. + tx.run(conn); + + // If we reach this point, commit the transaction, + // which implicitly releases the savepoint. + conn.commit(); + break; + } catch(SQLException e) { + // Check if the error code indicates a SERIALIZATION_FAILURE. + if(e.getErrorCode() == 40001) { + // Signal the database that we will attempt a retry. + conn.rollback(sp); + } + // This is a not a serialization failure, pass it up the chain. + throw e; + } + } + } + + public static void main(String[] args) throws ClassNotFoundException, SQLException { + // Load the postgres JDBC driver. + Class.forName("org.postgresql.Driver"); + + // Connect to the "bank" database. + Connection db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:26257/bank?sslmode=disable", "maxroach", ""); + try { + // We need to turn off autocommit mode to allow for + // multi-statement transactions. + db.setAutoCommit(false); + + RetryableTransaction transfer = transferFunds(1, 2, 100); + retryTransaction(db, transfer); + ResultSet res = db.createStatement().executeQuery("SELECT id, balance FROM accounts"); + while (res.next()) { + System.out.printf("\taccount %s: %s\n", res.getInt("id"), res.getInt("balance")); + } + } catch(InsufficientBalanceException ibe) { + System.out.println("Insufficient balance"); + } catch(SQLException sqle) { + System.out.println("SQLException encountered:" + sqle); + } catch(Exception e) { + System.out.println("Unknown exception encountered:" + e); + } finally { + // Close the database connection. + db.close(); + } + } +}