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:
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user