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.
|
* connection when it is released if appropriate.
|
||||||
*
|
*
|
||||||
* @param ident the database connection identifier.
|
* @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
|
* @return an active JDBC connection (which may have come from a
|
||||||
* connection pool).
|
* connection pool).
|
||||||
@@ -55,7 +57,7 @@ public interface ConnectionProvider
|
|||||||
* @exception PersistenceException thrown if a problem occurs trying
|
* @exception PersistenceException thrown if a problem occurs trying
|
||||||
* to open the requested connection.
|
* to open the requested connection.
|
||||||
*/
|
*/
|
||||||
public Connection getConnection (String ident)
|
public Connection getConnection (String ident, boolean readOnly)
|
||||||
throws PersistenceException;
|
throws PersistenceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,10 +66,13 @@ public interface ConnectionProvider
|
|||||||
*
|
*
|
||||||
* @param ident the database identifier used when obtaining this
|
* @param ident the database identifier used when obtaining this
|
||||||
* connection.
|
* 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
|
* @param conn the connection to release (back into the pool or to be
|
||||||
* closed if pooling is not going on).
|
* 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.
|
* 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
|
* @param ident the database identifier used when obtaining this
|
||||||
* connection.
|
* connection.
|
||||||
|
* @param readOnly the same value that was passed to {@link #getConnection}
|
||||||
|
* to obtain this connection.
|
||||||
* @param conn the connection that failed.
|
* @param conn the connection that failed.
|
||||||
* @param error the error thrown by the connection (which may be used
|
* @param error the error thrown by the connection (which may be used
|
||||||
* to determine if the connection should be closed or can be reused).
|
* to determine if the connection should be closed or can be reused).
|
||||||
*/
|
*/
|
||||||
public void connectionFailed (String ident, Connection conn,
|
public void connectionFailed (String ident, boolean readOnly,
|
||||||
SQLException error);
|
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)
|
protected <T> int insert (final Table<T> table, final T object)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
return execute(new Operation<Integer>() {
|
return executeUpdate(new Operation<Integer>() {
|
||||||
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,7 @@ public abstract class JORARepository extends SimpleRepository
|
|||||||
protected <T> void update (final Table<T> table, final T object)
|
protected <T> void update (final Table<T> table, final T object)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -188,7 +188,7 @@ public abstract class JORARepository extends SimpleRepository
|
|||||||
protected <T> int store (final Table<T> table, final T object)
|
protected <T> int store (final Table<T> table, final T object)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
return execute(new Operation<Integer>() {
|
return executeUpdate(new Operation<Integer>() {
|
||||||
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -211,7 +211,7 @@ public abstract class JORARepository extends SimpleRepository
|
|||||||
{
|
{
|
||||||
final FieldMask mask = table.getFieldMask();
|
final FieldMask mask = table.getFieldMask();
|
||||||
mask.setModified(field);
|
mask.setModified(field);
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -233,7 +233,7 @@ public abstract class JORARepository extends SimpleRepository
|
|||||||
for (int ii = 0; ii < fields.length; ii++) {
|
for (int ii = 0; ii < fields.length; ii++) {
|
||||||
mask.setModified(fields[ii]);
|
mask.setModified(fields[ii]);
|
||||||
}
|
}
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -246,7 +246,7 @@ public abstract class JORARepository extends SimpleRepository
|
|||||||
protected <T> void delete (final Table<T> table, final T object)
|
protected <T> void delete (final Table<T> table, final T object)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class SimpleRepository extends Repository
|
|||||||
// give the repository a chance to do any schema migration before
|
// give the repository a chance to do any schema migration before
|
||||||
// things get further underway
|
// things get further underway
|
||||||
try {
|
try {
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
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
|
* failure, the repository will attempt to reestablish the database
|
||||||
* connection and try the operation again.
|
* connection and try the operation again.
|
||||||
*
|
*
|
||||||
@@ -94,7 +94,20 @@ public class SimpleRepository extends Repository
|
|||||||
protected <V> V execute (Operation<V> op)
|
protected <V> V execute (Operation<V> op)
|
||||||
throws PersistenceException
|
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
|
* case a call to <code>rollback()</code> is executed on the
|
||||||
* connection.
|
* connection.
|
||||||
*
|
*
|
||||||
* @param retryOnTransientFailure if true and the operation fails due
|
* @param retryOnTransientFailure if true and the operation fails due to a
|
||||||
* to a transient failure (like losing the connection to the database
|
* transient failure (like losing the connection to the database or
|
||||||
* or deadlock detection), the connection to the database will be
|
* deadlock detection), the connection to the database will be
|
||||||
* reestablished (if necessary) and the operation attempted once more.
|
* 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.
|
* @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
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Connection conn = null;
|
Connection conn = null;
|
||||||
@@ -129,7 +144,7 @@ public class SimpleRepository extends Repository
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// obtain our database connection and associated goodies
|
// obtain our database connection and associated goodies
|
||||||
conn = _provider.getConnection(_dbident);
|
conn = _provider.getConnection(_dbident, readOnly);
|
||||||
liaison = LiaisonRegistry.getLiaison(conn);
|
liaison = LiaisonRegistry.getLiaison(conn);
|
||||||
|
|
||||||
// find out if we support transactions
|
// find out if we support transactions
|
||||||
@@ -172,7 +187,7 @@ public class SimpleRepository extends Repository
|
|||||||
|
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
// let the connection provider know that the connection failed
|
// 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
|
// clear out the reference so that we don't release it later
|
||||||
conn = null;
|
conn = null;
|
||||||
@@ -189,7 +204,7 @@ public class SimpleRepository extends Repository
|
|||||||
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);
|
return execute(op, false, readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
String err = "Operation invocation failed";
|
String err = "Operation invocation failed";
|
||||||
@@ -222,7 +237,7 @@ public class SimpleRepository extends Repository
|
|||||||
} finally {
|
} finally {
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
// release the database connection
|
// 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)
|
protected int update (final String query)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
return execute(new Operation<Integer>() {
|
return executeUpdate(new Operation<Integer>() {
|
||||||
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -257,7 +272,7 @@ public class SimpleRepository extends Repository
|
|||||||
protected void checkedUpdate (final String query, final int count)
|
protected void checkedUpdate (final String query, final int count)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
@@ -287,7 +302,7 @@ public class SimpleRepository extends Repository
|
|||||||
protected void maintenance (final String action, final String table)
|
protected void maintenance (final String action, final String table)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
execute(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -127,13 +127,15 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public Connection getConnection (String ident)
|
public Connection getConnection (String ident, boolean readOnly)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Mapping conmap = _idents.get(ident);
|
String mapkey = ident + ":" + readOnly;
|
||||||
|
Mapping conmap = _idents.get(mapkey);
|
||||||
|
|
||||||
// open the connection if we haven't already
|
// open the connection if we haven't already
|
||||||
if (conmap == null) {
|
if (conmap == null) {
|
||||||
|
String[] defaults;
|
||||||
Properties props =
|
Properties props =
|
||||||
PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
|
PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
|
||||||
|
|
||||||
@@ -147,9 +149,11 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
err = "No driver password specified [ident=" + ident + "].";
|
err = "No driver password specified [ident=" + ident + "].";
|
||||||
String password = requireProp(props, "password", err);
|
String password = requireProp(props, "password", err);
|
||||||
|
|
||||||
// we cache connections by username+url to avoid making more
|
// if this is a read-only connection,
|
||||||
// that one connection to a particular database server
|
|
||||||
String key = username + "@" + url;
|
// 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);
|
conmap = _keys.get(key);
|
||||||
if (conmap == null) {
|
if (conmap == null) {
|
||||||
Log.debug("Creating " + key + " for " + ident + ".");
|
Log.debug("Creating " + key + " for " + ident + ".");
|
||||||
@@ -157,42 +161,53 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
conmap.key = key;
|
conmap.key = key;
|
||||||
conmap.connection =
|
conmap.connection =
|
||||||
openConnection(driver, url, username, password);
|
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);
|
_keys.put(key, conmap);
|
||||||
} else {
|
} else {
|
||||||
Log.debug("Reusing " + key + " for " + ident + ".");
|
Log.debug("Reusing " + key + " for " + ident + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache the connection
|
// cache the connection
|
||||||
conmap.idents.add(ident);
|
conmap.idents.add(mapkey);
|
||||||
_idents.put(ident, conmap);
|
_idents.put(mapkey, conmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return conmap.connection;
|
return conmap.connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// 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
|
// nothing to do here, all is well
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void connectionFailed (String ident, Connection conn,
|
public void connectionFailed (String ident, boolean readOnly,
|
||||||
SQLException error)
|
Connection conn, SQLException error)
|
||||||
{
|
{
|
||||||
Mapping conmap = _idents.get(ident);
|
String mapkey = ident + ":" + readOnly;
|
||||||
|
Mapping conmap = _idents.get(mapkey);
|
||||||
if (conmap == null) {
|
if (conmap == null) {
|
||||||
Log.warning("Unknown connection failed!? [ident=" + ident + "].");
|
Log.warning("Unknown connection failed!? [ident=" + ident + "].");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to close the connection
|
// attempt to close the connection
|
||||||
try {
|
closeConnection(ident, conmap.connection);
|
||||||
conmap.connection.close();
|
|
||||||
} catch (SQLException sqe) {
|
|
||||||
Log.warning("Error closing failed connection [ident=" + ident +
|
|
||||||
", error=" + sqe + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear it from our mapping tables
|
// clear it from our mapping tables
|
||||||
for (int ii = 0; ii < conmap.idents.size(); ii++) {
|
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 (
|
protected static String requireProp (
|
||||||
Properties props, String name, String errmsg)
|
Properties props, String name, String errmsg)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
|
|||||||
Reference in New Issue
Block a user