We need to ensure that only one repository operation is active on a particular

JDBC connection at a time. JDBC connections are thread safe, but we frequently
have to execute statements like:

insert into FOO values(0, blah, blah);
select LAST_INSERTED_ID()

to get the value assigned to an auto-increment column. If another thread
slipped in between those statements and inserted something into its own
auto-increment column having table, we'd get funny business. And indeed we very
occasionally see this race condition happen in the thread-happy webapps.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1838 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2006-04-29 18:54:47 +00:00
parent 5fc9cafc36
commit 7a4588e859
@@ -142,9 +142,13 @@ public class SimpleRepository extends Repository
Thread.dumpStack(); Thread.dumpStack();
} }
try {
// obtain our database connection and associated goodies // obtain our database connection and associated goodies
conn = _provider.getConnection(_dbident, readOnly); conn = _provider.getConnection(_dbident, readOnly);
// make sure that no one else performs a database operation using the
// same connection until we're done
synchronized (conn) {
try {
liaison = LiaisonRegistry.getLiaison(conn); liaison = LiaisonRegistry.getLiaison(conn);
// find out if we support transactions // find out if we support transactions
@@ -181,34 +185,30 @@ public class SimpleRepository extends Repository
} }
} catch (SQLException rbe) { } catch (SQLException rbe) {
Log.warning("Unable to roll back operation " + Log.warning("Unable to roll back operation " +
"[origerr=" + sqe + ", rberr=" + rbe + "]."); "[err=" + sqe + ", rberr=" + rbe + "].");
} }
} }
if (conn != null) { if (conn != null) {
// let the connection provider know that the connection failed // let the provider know that the connection failed
_provider.connectionFailed(_dbident, readOnly, conn, sqe); _provider.connectionFailed(_dbident, readOnly, conn, sqe);
// clear out the reference so that we don't release it later // clear out the reference so that we don't release it later
conn = null; conn = null;
} }
// if this is a transient failure and we've been requested to if (!retryOnTransientFailure || liaison == null ||
// retry such failures, try one more time !liaison.isTransientException(sqe)) {
if (retryOnTransientFailure && String err = "Operation invocation failed";
liaison != null && liaison.isTransientException(sqe)) { throw new PersistenceException(err, sqe);
// the MySQL JDBC driver has the annoying habit of }
// including the embedded exception stack trace in the
// message of their outer exception; if I want a fucking // the MySQL JDBC driver has the annoying habit of including
// stack trace, I'll call printStackTrace() thanksverymuch // the embedded exception stack trace in the message of their
// outer exception; if I want a fucking stack trace, I'll call
// printStackTrace() thanksverymuch
String msg = StringUtil.split("" + sqe, "\n")[0]; String msg = StringUtil.split("" + sqe, "\n")[0];
Log.info("Transient failure executing operation, " + Log.info("Transient failure executing operation, " +
"retrying [error=" + msg + "]."); "retrying [error=" + msg + "].");
return execute(op, false, readOnly);
}
String err = "Operation invocation failed";
throw new PersistenceException(err, sqe);
} catch (PersistenceException pe) { } catch (PersistenceException pe) {
// back out our changes if something got hosed // back out our changes if something got hosed
@@ -225,7 +225,8 @@ public class SimpleRepository extends Repository
} catch (RuntimeException rte) { } catch (RuntimeException rte) {
// back out our changes if something got hosed // back out our changes if something got hosed
try { try {
if (conn != null && supportsTransactions && !conn.isClosed()) { if (supportsTransactions &&
conn != null && !conn.isClosed()) {
conn.rollback(); conn.rollback();
} }
} catch (SQLException rbe) { } catch (SQLException rbe) {
@@ -242,6 +243,12 @@ public class SimpleRepository extends Repository
} }
} }
// we'll only fall through here if the above code failed due to a
// transient exception (the connection was closed for being idle, for
// example) and we've been asked to retry; so let's do so
return execute(op, false, readOnly);
}
/** /**
* Executes the supplied update query in this repository, returning * Executes the supplied update query in this repository, returning
* the number of rows modified. * the number of rows modified.