Hi
I am using H2 in a clustered configuration where we need some
control/detection of which servers are currently in the cluster. I have
added functionality to the jdbc connection to get the number of servers
currently in the cluster and which servers that are available. Have a look
at my patch and tell me what you think.
Best regards
Nikolaj Fogh
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.
Index: src/docsrc/html/advanced.html
===================================================================
--- src/docsrc/html/advanced.html (revision 5747)
+++ src/docsrc/html/advanced.html (working copy)
@@ -390,6 +390,15 @@
servers is returned, enclosed in single quote. Example: <code>'server1:9191,server2:9191'</code>.
</p>
+<p>It is also possible to get the list of servers by using Connection.getClientInfo().</p>
+
+<p>The property list returned from <code>getClientInfo()</code> contains a <code>numServers</code> property that returns the
+number of servers that are in the connection list. To get the actual servers, <code>getClientInfo()</code> also has
+properties <code>server0</code>..<code>serverX</code>, where serverX is the number of servers minus 1.
+
+<p>Example: To get the 2nd server in the connection list one uses <code>getClientInfo('server1')<code>. <b>Note:</b> The
+<code>serverX</code> property only returns IP addresses and ports and not hostnames.</p>
+
<h3>Clustering Algorithm and Limitations</h3>
<p>
Read-only queries are only executed against the first cluster node, but all other statements are
Index: src/main/org/h2/engine/Session.java
===================================================================
--- src/main/org/h2/engine/Session.java (revision 5747)
+++ src/main/org/h2/engine/Session.java (working copy)
@@ -127,6 +127,10 @@
this.currentSchemaName = Constants.SCHEMA_MAIN;
}
+ public ArrayList<String> getServers() {
+ return new ArrayList<String>();
+ }
+
public boolean setCommitOrRollbackDisabled(boolean x) {
boolean old = commitOrRollbackDisabled;
commitOrRollbackDisabled = x;
Index: src/main/org/h2/engine/SessionInterface.java
===================================================================
--- src/main/org/h2/engine/SessionInterface.java (revision 5747)
+++ src/main/org/h2/engine/SessionInterface.java (working copy)
@@ -6,6 +6,8 @@
package org.h2.engine;
import java.io.Closeable;
+import java.util.ArrayList;
+
import org.h2.command.CommandInterface;
import org.h2.message.Trace;
import org.h2.store.DataHandler;
@@ -17,6 +19,13 @@
public interface SessionInterface extends Closeable {
/**
+ * Get the list of servers for this session.
+ *
+ * @return A list of "IP:PORT" strings for the servers in this session.
+ */
+ ArrayList<String> getServers();
+
+ /**
* Parse a command and prepare it for execution.
*
* @param sql the SQL statement
Index: src/main/org/h2/engine/SessionRemote.java
===================================================================
--- src/main/org/h2/engine/SessionRemote.java (revision 5747)
+++ src/main/org/h2/engine/SessionRemote.java (working copy)
@@ -8,6 +8,7 @@
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.List;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
@@ -95,6 +96,17 @@
this.connectionInfo = ci;
}
+ public ArrayList<String> getServers() {
+ ArrayList<String> serverList = new ArrayList<String>();
+
+ for (int i = 0; i < transferList.size(); i++) {
+ Transfer transfer = transferList.get(i);
+ serverList.add(transfer.getSocket().getInetAddress().getHostAddress().toString() + ":" + String.valueOf(transfer.getSocket().getPort()));
+ }
+
+ return serverList;
+ }
+
private Transfer initTransfer(ConnectionInfo ci, String db, String server)
throws IOException {
Socket socket = NetUtils.createSocket(server,
Index: src/main/org/h2/jdbc/JdbcConnection.java
===================================================================
--- src/main/org/h2/jdbc/JdbcConnection.java (revision 5747)
+++ src/main/org/h2/jdbc/JdbcConnection.java (working copy)
@@ -25,6 +25,7 @@
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
+import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
@@ -47,7 +48,7 @@
import org.h2.value.ValueNull;
import org.h2.value.ValueString;
-/*## Java 1.7 ##
+//## Java 1.7 ##
import java.util.concurrent.Executor;
//*/
@@ -1715,35 +1716,37 @@
/**
* Get the client properties.
- * This method always returns null.
*
- * @return always null
+ * @return the property list
*/
@Override
public Properties getClientInfo() throws SQLException {
try {
- debugCode("getClientInfo();");
- // we don't have any client properties, so return null
- return null;
+ ArrayList<String> serverList = session.getServers();
+ Properties p = new Properties();
+
+ p.setProperty("numServers", String.valueOf(serverList.size()));
+ for (int i = 0; i < serverList.size(); i++)
+ p.setProperty("server" + String.valueOf(i), serverList.get(i));
+ return p;
} catch (Exception e) {
throw logAndConvert(e);
}
}
/**
- * Set a client property.
- * This method always throws a SQLClientInfoException.
+ * Get a client property.
*
* @param name the client info name (ignored)
- * @return this method never returns normally
+ * @return the property value
*/
@Override
public String getClientInfo(String name) throws SQLException {
try {
+ Properties p = getClientInfo();
debugCodeCall("getClientInfo", name);
checkClosed();
- // we don't have any client properties, so just throw
- throw new SQLClientInfoException();
+ return p.getProperty(name);
} catch (Exception e) {
throw logAndConvert(e);
}
@@ -1814,7 +1817,7 @@
*
* @param schema the schema
*/
-/*## Java 1.7 ##
+//## Java 1.7 ##
@Override
public void setSchema(String schema) {
// not supported
@@ -1824,7 +1827,7 @@
/**
* [Not supported]
*/
-/*## Java 1.7 ##
+//## Java 1.7 ##
@Override
public String getSchema() {
return null;
@@ -1836,7 +1839,7 @@
*
* @param executor the executor used by this method
*/
-/*## Java 1.7 ##
+//## Java 1.7 ##
@Override
public void abort(Executor executor) {
// not supported
@@ -1849,7 +1852,7 @@
* @param executor the executor used by this method
* @param milliseconds the TCP connection timeout
*/
-/*## Java 1.7 ##
+//## Java 1.7 ##
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) {
// not supported
@@ -1859,7 +1862,7 @@
/**
* [Not supported]
*/
-/*## Java 1.7 ##
+//## Java 1.7 ##
@Override
public int getNetworkTimeout() {
return 0;
Index: src/test/org/h2/test/db/TestCluster.java
===================================================================
--- src/test/org/h2/test/db/TestCluster.java (revision 5747)
+++ src/test/org/h2/test/db/TestCluster.java (working copy)
@@ -10,6 +10,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Properties;
import org.h2.api.ErrorCode;
import org.h2.store.fs.FileUtils;
@@ -39,6 +40,7 @@
testRecover();
testRollback();
testCase();
+ testClientInfo();
testCreateClusterAtRuntime();
testStartStopCluster();
}
@@ -250,6 +252,60 @@
n1.stop();
deleteFiles();
}
+
+ private void testClientInfo() throws SQLException {
+ if (config.memory || config.networked || config.cipher != null) {
+ return;
+ }
+ int port1 = 9191, port2 = 9192;
+ String serverList = "localhost:" + port1 + ",localhost:" + port2;
+ deleteFiles();
+
+ org.h2.Driver.load();
+ String user = getUser(), password = getPassword();
+ Connection conn;
+
+ String url1 = getURL("jdbc:h2:tcp://localhost:" + port1 + "/test", true);
+ String url2 = getURL("jdbc:h2:tcp://localhost:" + port2 + "/test", true);
+ String urlCluster = getURL("jdbc:h2:tcp://" + serverList + "/test", true);
+
+ Server n1 = org.h2.tools.Server.createTcpServer("-tcpPort",
+ "" + port1, "-baseDir", getBaseDir() + "/node1").start();
+ Server n2 = org.h2.tools.Server.createTcpServer("-tcpPort",
+ "" + port2 , "-baseDir", getBaseDir() + "/node2").start();
+
+ CreateCluster.main("-urlSource", url1, "-urlTarget", url2,
+ "-user", user, "-password", password, "-serverList",
+ serverList);
+
+ conn = getConnection(urlCluster, user, password);
+ Properties p = conn.getClientInfo();
+
+ assertEquals("2", p.getProperty("numServers"));
+ assertEquals("127.0.0.1:" + port1, p.getProperty("server0"));
+ assertEquals("127.0.0.1:" + port2, p.getProperty("server1"));
+
+ assertEquals("2", conn.getClientInfo("numServers"));
+ assertEquals("127.0.0.1:" + port1, conn.getClientInfo("server0"));
+ assertEquals("127.0.0.1:" + port2, conn.getClientInfo("server1"));
+ conn.close();
+
+ // stop server 2, and test if only one server is available
+ n2.stop();
+
+ conn = getConnection(urlCluster, user, password);
+ p = conn.getClientInfo();
+
+ assertEquals("1", p.getProperty("numServers"));
+ assertEquals("127.0.0.1:" + port1, p.getProperty("server0"));
+ assertEquals("1", conn.getClientInfo("numServers"));
+ assertEquals("127.0.0.1:" + port1, conn.getClientInfo("server0"));
+ conn.close();
+
+ n1.stop();
+ deleteFiles();
+ }
+
private void testCreateClusterAtRuntime() throws SQLException {
if (config.memory || config.networked || config.cipher != null) {
return;