Require a distinction to be made between read-only and read-write queries so
that we can route read-only queries to one or more read-only mirrors of a database (the MySQL database drivers supports round-robin load balancing of queries to read-only slaves). git-svn-id: https://samskivert.googlecode.com/svn/trunk@1822 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -48,6 +48,8 @@ public interface ConnectionProvider
|
||||
* connection when it is released if appropriate.
|
||||
*
|
||||
* @param ident the database connection identifier.
|
||||
* @param readOnly whether or not the connection may be to a read-only
|
||||
* mirror of the repository.
|
||||
*
|
||||
* @return an active JDBC connection (which may have come from a
|
||||
* connection pool).
|
||||
@@ -55,7 +57,7 @@ public interface ConnectionProvider
|
||||
* @exception PersistenceException thrown if a problem occurs trying
|
||||
* to open the requested connection.
|
||||
*/
|
||||
public Connection getConnection (String ident)
|
||||
public Connection getConnection (String ident, boolean readOnly)
|
||||
throws PersistenceException;
|
||||
|
||||
/**
|
||||
@@ -64,10 +66,13 @@ public interface ConnectionProvider
|
||||
*
|
||||
* @param ident the database identifier used when obtaining this
|
||||
* connection.
|
||||
* @param readOnly the same value that was passed to {@link #getConnection}
|
||||
* to obtain this connection.
|
||||
* @param conn the connection to release (back into the pool or to be
|
||||
* closed if pooling is not going on).
|
||||
*/
|
||||
public void releaseConnection (String ident, Connection conn);
|
||||
public void releaseConnection (String ident, boolean readOnly,
|
||||
Connection conn);
|
||||
|
||||
/**
|
||||
* Called by the repository if a failure occurred on the connection.
|
||||
@@ -77,10 +82,12 @@ public interface ConnectionProvider
|
||||
*
|
||||
* @param ident the database identifier used when obtaining this
|
||||
* connection.
|
||||
* @param readOnly the same value that was passed to {@link #getConnection}
|
||||
* to obtain this connection.
|
||||
* @param conn the connection that failed.
|
||||
* @param error the error thrown by the connection (which may be used
|
||||
* to determine if the connection should be closed or can be reused).
|
||||
*/
|
||||
public void connectionFailed (String ident, Connection conn,
|
||||
SQLException error);
|
||||
public void connectionFailed (String ident, boolean readOnly,
|
||||
Connection conn, SQLException error);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public abstract class JORARepository extends SimpleRepository
|
||||
protected <T> int insert (final Table<T> table, final T object)
|
||||
throws PersistenceException
|
||||
{
|
||||
return execute(new Operation<Integer>() {
|
||||
return executeUpdate(new Operation<Integer>() {
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -79,7 +79,7 @@ public abstract class JORARepository extends SimpleRepository
|
||||
protected <T> void update (final Table<T> table, final T object)
|
||||
throws PersistenceException
|
||||
{
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -188,7 +188,7 @@ public abstract class JORARepository extends SimpleRepository
|
||||
protected <T> int store (final Table<T> table, final T object)
|
||||
throws PersistenceException
|
||||
{
|
||||
return execute(new Operation<Integer>() {
|
||||
return executeUpdate(new Operation<Integer>() {
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -211,7 +211,7 @@ public abstract class JORARepository extends SimpleRepository
|
||||
{
|
||||
final FieldMask mask = table.getFieldMask();
|
||||
mask.setModified(field);
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -233,7 +233,7 @@ public abstract class JORARepository extends SimpleRepository
|
||||
for (int ii = 0; ii < fields.length; ii++) {
|
||||
mask.setModified(fields[ii]);
|
||||
}
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -246,7 +246,7 @@ public abstract class JORARepository extends SimpleRepository
|
||||
protected <T> void delete (final Table<T> table, final T object)
|
||||
throws PersistenceException
|
||||
{
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ public class SimpleRepository extends Repository
|
||||
// give the repository a chance to do any schema migration before
|
||||
// things get further underway
|
||||
try {
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -85,7 +85,7 @@ public class SimpleRepository extends Repository
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the supplied operation. In the event of a transient
|
||||
* Executes the supplied read-only operation. In the event of a transient
|
||||
* failure, the repository will attempt to reestablish the database
|
||||
* connection and try the operation again.
|
||||
*
|
||||
@@ -94,7 +94,20 @@ public class SimpleRepository extends Repository
|
||||
protected <V> V execute (Operation<V> op)
|
||||
throws PersistenceException
|
||||
{
|
||||
return execute(op, true);
|
||||
return execute(op, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the supplied read-write operation. In the event of a transient
|
||||
* failure, the repository will attempt to reestablish the database
|
||||
* connection and try the operation again.
|
||||
*
|
||||
* @return whatever value is returned by the invoked operation.
|
||||
*/
|
||||
protected <V> V executeUpdate (Operation<V> op)
|
||||
throws PersistenceException
|
||||
{
|
||||
return execute(op, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,14 +117,16 @@ public class SimpleRepository extends Repository
|
||||
* case a call to <code>rollback()</code> is executed on the
|
||||
* connection.
|
||||
*
|
||||
* @param retryOnTransientFailure if true and the operation fails due
|
||||
* to a transient failure (like losing the connection to the database
|
||||
* or deadlock detection), the connection to the database will be
|
||||
* @param retryOnTransientFailure if true and the operation fails due to a
|
||||
* transient failure (like losing the connection to the database or
|
||||
* deadlock detection), the connection to the database will be
|
||||
* reestablished (if necessary) and the operation attempted once more.
|
||||
* @param readOnly whether or not to request a read-only connection.
|
||||
*
|
||||
* @return whatever value is returned by the invoked operation.
|
||||
*/
|
||||
protected <V> V execute (Operation<V> op, boolean retryOnTransientFailure)
|
||||
protected <V> V execute (Operation<V> op, boolean retryOnTransientFailure,
|
||||
boolean readOnly)
|
||||
throws PersistenceException
|
||||
{
|
||||
Connection conn = null;
|
||||
@@ -129,7 +144,7 @@ public class SimpleRepository extends Repository
|
||||
|
||||
try {
|
||||
// obtain our database connection and associated goodies
|
||||
conn = _provider.getConnection(_dbident);
|
||||
conn = _provider.getConnection(_dbident, readOnly);
|
||||
liaison = LiaisonRegistry.getLiaison(conn);
|
||||
|
||||
// find out if we support transactions
|
||||
@@ -172,7 +187,7 @@ public class SimpleRepository extends Repository
|
||||
|
||||
if (conn != null) {
|
||||
// let the connection provider know that the connection failed
|
||||
_provider.connectionFailed(_dbident, conn, sqe);
|
||||
_provider.connectionFailed(_dbident, readOnly, conn, sqe);
|
||||
|
||||
// clear out the reference so that we don't release it later
|
||||
conn = null;
|
||||
@@ -189,7 +204,7 @@ public class SimpleRepository extends Repository
|
||||
String msg = StringUtil.split("" + sqe, "\n")[0];
|
||||
Log.info("Transient failure executing operation, " +
|
||||
"retrying [error=" + msg + "].");
|
||||
return execute(op, false);
|
||||
return execute(op, false, readOnly);
|
||||
}
|
||||
|
||||
String err = "Operation invocation failed";
|
||||
@@ -222,7 +237,7 @@ public class SimpleRepository extends Repository
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
// release the database connection
|
||||
_provider.releaseConnection(_dbident, conn);
|
||||
_provider.releaseConnection(_dbident, readOnly, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,7 +249,7 @@ public class SimpleRepository extends Repository
|
||||
protected int update (final String query)
|
||||
throws PersistenceException
|
||||
{
|
||||
return execute(new Operation<Integer>() {
|
||||
return executeUpdate(new Operation<Integer>() {
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -257,7 +272,7 @@ public class SimpleRepository extends Repository
|
||||
protected void checkedUpdate (final String query, final int count)
|
||||
throws PersistenceException
|
||||
{
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
@@ -287,7 +302,7 @@ public class SimpleRepository extends Repository
|
||||
protected void maintenance (final String action, final String table)
|
||||
throws PersistenceException
|
||||
{
|
||||
execute(new Operation<Object>() {
|
||||
executeUpdate(new Operation<Object>() {
|
||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException, PersistenceException
|
||||
{
|
||||
|
||||
@@ -127,13 +127,15 @@ public class StaticConnectionProvider implements ConnectionProvider
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Connection getConnection (String ident)
|
||||
public Connection getConnection (String ident, boolean readOnly)
|
||||
throws PersistenceException
|
||||
{
|
||||
Mapping conmap = _idents.get(ident);
|
||||
String mapkey = ident + ":" + readOnly;
|
||||
Mapping conmap = _idents.get(mapkey);
|
||||
|
||||
// open the connection if we haven't already
|
||||
if (conmap == null) {
|
||||
String[] defaults;
|
||||
Properties props =
|
||||
PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
|
||||
|
||||
@@ -147,9 +149,11 @@ public class StaticConnectionProvider implements ConnectionProvider
|
||||
err = "No driver password specified [ident=" + ident + "].";
|
||||
String password = requireProp(props, "password", err);
|
||||
|
||||
// we cache connections by username+url to avoid making more
|
||||
// that one connection to a particular database server
|
||||
String key = username + "@" + url;
|
||||
// if this is a read-only connection,
|
||||
|
||||
// we cache connections by username+url+readOnly to avoid making
|
||||
// more that one connection to a particular database server
|
||||
String key = username + "@" + url + ":" + readOnly;
|
||||
conmap = _keys.get(key);
|
||||
if (conmap == null) {
|
||||
Log.debug("Creating " + key + " for " + ident + ".");
|
||||
@@ -157,42 +161,53 @@ public class StaticConnectionProvider implements ConnectionProvider
|
||||
conmap.key = key;
|
||||
conmap.connection =
|
||||
openConnection(driver, url, username, password);
|
||||
|
||||
// make the connection read-only to let the JDBC driver know
|
||||
// that it can and should use the read-only mirror(s)
|
||||
if (readOnly) {
|
||||
try {
|
||||
conmap.connection.setReadOnly(true);
|
||||
} catch (SQLException sqe) {
|
||||
closeConnection(ident, conmap.connection);
|
||||
err = "Failed to make connection read-only " +
|
||||
"[key=" + key + ", ident=" + ident + "].";
|
||||
throw new PersistenceException(err, sqe);
|
||||
}
|
||||
}
|
||||
|
||||
_keys.put(key, conmap);
|
||||
} else {
|
||||
Log.debug("Reusing " + key + " for " + ident + ".");
|
||||
}
|
||||
|
||||
// cache the connection
|
||||
conmap.idents.add(ident);
|
||||
_idents.put(ident, conmap);
|
||||
conmap.idents.add(mapkey);
|
||||
_idents.put(mapkey, conmap);
|
||||
}
|
||||
|
||||
return conmap.connection;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void releaseConnection (String ident, Connection conn)
|
||||
public void releaseConnection (String ident, boolean readOnly,
|
||||
Connection conn)
|
||||
{
|
||||
// nothing to do here, all is well
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void connectionFailed (String ident, Connection conn,
|
||||
SQLException error)
|
||||
public void connectionFailed (String ident, boolean readOnly,
|
||||
Connection conn, SQLException error)
|
||||
{
|
||||
Mapping conmap = _idents.get(ident);
|
||||
String mapkey = ident + ":" + readOnly;
|
||||
Mapping conmap = _idents.get(mapkey);
|
||||
if (conmap == null) {
|
||||
Log.warning("Unknown connection failed!? [ident=" + ident + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// attempt to close the connection
|
||||
try {
|
||||
conmap.connection.close();
|
||||
} catch (SQLException sqe) {
|
||||
Log.warning("Error closing failed connection [ident=" + ident +
|
||||
", error=" + sqe + "].");
|
||||
}
|
||||
closeConnection(ident, conmap.connection);
|
||||
|
||||
// clear it from our mapping tables
|
||||
for (int ii = 0; ii < conmap.idents.size(); ii++) {
|
||||
@@ -224,6 +239,16 @@ public class StaticConnectionProvider implements ConnectionProvider
|
||||
}
|
||||
}
|
||||
|
||||
protected void closeConnection (String ident, Connection conn)
|
||||
{
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException sqe) {
|
||||
Log.warning("Error closing failed connection [ident=" + ident +
|
||||
", error=" + sqe + "].");
|
||||
}
|
||||
}
|
||||
|
||||
protected static String requireProp (
|
||||
Properties props, String name, String errmsg)
|
||||
throws PersistenceException
|
||||
|
||||
Reference in New Issue
Block a user