提交 51343390 authored 作者: Thomas Mueller's avatar Thomas Mueller

Cluster: after a cluster node failed, the second cluster node can now be re-created.

上级 9950bf37
...@@ -82,6 +82,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D ...@@ -82,6 +82,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
private SessionInterface embedded; private SessionInterface embedded;
private DatabaseEventListener eventListener; private DatabaseEventListener eventListener;
private LobStorage lobStorage; private LobStorage lobStorage;
private boolean cluster;
public SessionRemote() { public SessionRemote() {
// nothing to do // nothing to do
...@@ -161,6 +162,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D ...@@ -161,6 +162,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
switchOffAutoCommit.executeUpdate(); switchOffAutoCommit.executeUpdate();
// so we need to switch it on // so we need to switch it on
autoCommit = true; autoCommit = true;
cluster = true;
} }
} }
...@@ -292,10 +294,11 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D ...@@ -292,10 +294,11 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
} }
autoReconnect = Boolean.valueOf(ci.getProperty("AUTO_RECONNECT", "false")).booleanValue(); autoReconnect = Boolean.valueOf(ci.getProperty("AUTO_RECONNECT", "false")).booleanValue();
// AUTO_SERVER implies AUTO_RECONNECT // AUTO_SERVER implies AUTO_RECONNECT
autoReconnect |= Boolean.valueOf(ci.getProperty("AUTO_SERVER", "false")).booleanValue(); boolean autoServer = Boolean.valueOf(ci.getProperty("AUTO_SERVER", "false")).booleanValue();
if (autoReconnect && serverList != null) { if (autoServer && serverList != null) {
throw DbException.getUnsupportedException("autoReconnect && serverList != null"); throw DbException.getUnsupportedException("autoServer && serverList != null");
} }
autoReconnect |= autoServer;
if (autoReconnect) { if (autoReconnect) {
String className = ci.getProperty("DATABASE_EVENT_LISTENER"); String className = ci.getProperty("DATABASE_EVENT_LISTENER");
if (className != null) { if (className != null) {
...@@ -390,7 +393,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D ...@@ -390,7 +393,7 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
*/ */
public void removeServer(IOException e, int i, int count) { public void removeServer(IOException e, int i, int count) {
transferList.remove(i); transferList.remove(i);
if (autoReconnect(count)) { if (transferList.size() == 0 && autoReconnect(count)) {
return; return;
} }
checkClosed(); checkClosed();
...@@ -414,7 +417,10 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D ...@@ -414,7 +417,10 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
if (!isClosed()) { if (!isClosed()) {
return false; return false;
} }
if (!autoReconnect || !autoCommit) { if (!autoReconnect) {
return false;
}
if (!cluster && !autoCommit) {
return false; return false;
} }
if (count > SysProperties.MAX_RECONNECT) { if (count > SysProperties.MAX_RECONNECT) {
...@@ -504,7 +510,14 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D ...@@ -504,7 +510,14 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
String sql = transfer.readString(); String sql = transfer.readString();
int errorCode = transfer.readInt(); int errorCode = transfer.readInt();
String stackTrace = transfer.readString(); String stackTrace = transfer.readString();
throw DbException.convert(new JdbcSQLException(message, sql, sqlstate, errorCode, null, stackTrace)); JdbcSQLException s = new JdbcSQLException(message, sql, sqlstate, errorCode, null, stackTrace);
if (errorCode == ErrorCode.CONNECTION_BROKEN_1) {
// allow re-connect
IOException e = new IOException(s.toString());
e.initCause(s);
throw e;
}
throw DbException.convert(s);
} else if (status == STATUS_CLOSED) { } else if (status == STATUS_CLOSED) {
transferList = null; transferList = null;
} else if (status == STATUS_OK_STATE_CHANGED) { } else if (status == STATUS_OK_STATE_CHANGED) {
......
...@@ -854,7 +854,6 @@ public class MetaTable extends Table { ...@@ -854,7 +854,6 @@ public class MetaTable extends Table {
add(rows, "property." + s, SysProperties.getStringSetting(s, "")); add(rows, "property." + s, SysProperties.getStringSetting(s, ""));
} }
} }
add(rows, "CLUSTER", database.getCluster());
add(rows, "EXCLUSIVE", database.getExclusiveSession() == null ? "FALSE" : "TRUE"); add(rows, "EXCLUSIVE", database.getExclusiveSession() == null ? "FALSE" : "TRUE");
add(rows, "MODE", database.getMode().getName()); add(rows, "MODE", database.getMode().getName());
add(rows, "MULTI_THREADED", database.isMultiThreaded() ? "1" : "0"); add(rows, "MULTI_THREADED", database.isMultiThreaded() ? "1" : "0");
......
...@@ -11,7 +11,6 @@ import java.sql.Connection; ...@@ -11,7 +11,6 @@ import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.Tool; import org.h2.util.Tool;
...@@ -120,9 +119,11 @@ public class CreateCluster extends Tool { ...@@ -120,9 +119,11 @@ public class CreateCluster extends Tool {
connSource = DriverManager.getConnection(urlSource + ";CLUSTER=''", user, password); connSource = DriverManager.getConnection(urlSource + ";CLUSTER=''", user, password);
statSource = connSource.createStatement(); statSource = connSource.createStatement();
// enable the exclusive mode, so that other applications // enable the exclusive mode and close other connections,
// cannot change the data while it is restoring the second database // so that data can't change while restoring the second database
statSource.execute("SET EXCLUSIVE TRUE"); statSource.execute("SET EXCLUSIVE 2");
try {
// backup // backup
Script script = new Script(); Script script = new Script();
...@@ -135,7 +136,12 @@ public class CreateCluster extends Tool { ...@@ -135,7 +136,12 @@ public class CreateCluster extends Tool {
IOUtils.closeSilently(scriptOut); IOUtils.closeSilently(scriptOut);
} }
// restore // delete the target database and then restore
connTarget = DriverManager.getConnection(urlTarget + ";CLUSTER=''", user, password);
statTarget = connTarget.createStatement();
statTarget.execute("DROP ALL OBJECTS DELETE FILES");
connTarget.close();
RunScript runScript = new RunScript(); RunScript runScript = new RunScript();
runScript.setOut(out); runScript.setOut(out);
runScript.process(urlTarget, user, password, scriptFile, null, false); runScript.process(urlTarget, user, password, scriptFile, null, false);
...@@ -146,8 +152,11 @@ public class CreateCluster extends Tool { ...@@ -146,8 +152,11 @@ public class CreateCluster extends Tool {
// set the cluster to the serverList on both databases // set the cluster to the serverList on both databases
statSource.executeUpdate("SET CLUSTER '" + serverList + "'"); statSource.executeUpdate("SET CLUSTER '" + serverList + "'");
statTarget.executeUpdate("SET CLUSTER '" + serverList + "'"); statTarget.executeUpdate("SET CLUSTER '" + serverList + "'");
} finally {
// switch back to the regular mode
statSource.execute("SET EXCLUSIVE FALSE"); statSource.execute("SET EXCLUSIVE FALSE");
}
} finally { } finally {
IOUtils.delete(scriptFile); IOUtils.delete(scriptFile);
JdbcUtils.closeSilently(statSource); JdbcUtils.closeSilently(statSource);
......
...@@ -17,6 +17,7 @@ import org.h2.test.TestBase; ...@@ -17,6 +17,7 @@ import org.h2.test.TestBase;
import org.h2.tools.CreateCluster; import org.h2.tools.CreateCluster;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server; import org.h2.tools.Server;
import org.h2.util.JdbcUtils;
/** /**
* Test for the cluster feature. * Test for the cluster feature.
...@@ -32,15 +33,84 @@ public class TestCluster extends TestBase { ...@@ -32,15 +33,84 @@ public class TestCluster extends TestBase {
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
public void test() throws SQLException { public void test() throws Exception {
testCreateClusterAtRuntime();
testStartStopCluster();
}
private void testCreateClusterAtRuntime() throws SQLException {
if (config.memory || config.networked || config.cipher != null) { if (config.memory || config.networked || config.cipher != null) {
return; return;
} }
int port1 = 9191, port2 = 9192;
String serverList = "localhost:" + port1 + ",localhost:" + port2;
deleteFiles(); deleteFiles();
// create the master database org.h2.Driver.load();
String user = getUser(), password = getPassword();
Connection conn;
Statement stat;
String url1 = "jdbc:h2:tcp://localhost:" + port1 + "/test";
String url2 = "jdbc:h2:tcp://localhost:" + port2 + "/test";
String urlCluster = "jdbc:h2:tcp://" + serverList + "/test";
int len = 10;
// initialize the database
Server n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port1, "-baseDir", baseDir + "/node1").start();
conn = DriverManager.getConnection(url1, user, password);
stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar) as " +
"select x, 'Data' || x from system_range(0, " + (len - 1) + ")");
// start the second server
Server n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port2 , "-baseDir", baseDir + "/node2").start();
// copy the database and initialize the cluster
CreateCluster.main("-urlSource", url1, "-urlTarget", url2, "-user", user, "-password", password, "-serverList",
serverList);
// check the original connection is closed
try {
stat.execute("select * from test");
fail();
} catch (SQLException e) {
// expected
JdbcUtils.closeSilently(conn);
}
// test the cluster connection
Connection connApp = DriverManager.getConnection(urlCluster + ";AUTO_RECONNECT=TRUE", user, password);
check(connApp, len, "'" + serverList + "'");
// stop server 2, and test if only one server is available
n2.stop();
check(connApp, len, "''");
// re-create the cluster
n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port2, "-baseDir", baseDir + "/node2").start();
CreateCluster.main("-urlSource", url1, "-urlTarget", url2, "-user", user, "-password", password, "-serverList",
serverList);
// test the cluster connection
check(connApp, len, "'" + serverList + "'");
n1.stop();
n2.stop();
deleteFiles();
}
private void testStartStopCluster() throws SQLException {
if (config.memory || config.networked || config.cipher != null) {
return;
}
int port1 = 9193, port2 = 9194;
String serverList = "localhost:" + port1 + ",localhost:" + port2;
deleteFiles();
// initialize the database
Connection conn; Connection conn;
org.h2.Driver.load(); org.h2.Driver.load();
String urlNode1 = getURL("node1/test", true); String urlNode1 = getURL("node1/test", true);
String urlNode2 = getURL("node2/test", true); String urlNode2 = getURL("node2/test", true);
String user = getUser(), password = getPassword(); String user = getUser(), password = getPassword();
...@@ -59,39 +129,42 @@ public class TestCluster extends TestBase { ...@@ -59,39 +129,42 @@ public class TestCluster extends TestBase {
check(conn, len, "''"); check(conn, len, "''");
conn.close(); conn.close();
// copy the database and initialize the cluster
CreateCluster.main("-urlSource", urlNode1, "-urlTarget", CreateCluster.main("-urlSource", urlNode1, "-urlTarget",
urlNode2, "-user", user, "-password", password, "-serverList", urlNode2, "-user", user, "-password", password, "-serverList",
"localhost:9191,localhost:9192"); serverList);
Server n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "9191", "-baseDir", baseDir + "/node1").start();
Server n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "9192", "-baseDir", baseDir + "/node2").start();
// start both servers
Server n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port1, "-baseDir", baseDir + "/node1").start();
Server n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port2, "-baseDir", baseDir + "/node2").start();
// try to connect in standalone mode - should fail
try { try {
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9191/test", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:"+port1+"/test", user, password);
fail("should not be able to connect in standalone mode"); fail("should not be able to connect in standalone mode");
} catch (SQLException e) { } catch (SQLException e) {
assertKnownException(e); assertKnownException(e);
} }
try { try {
DriverManager.getConnection("jdbc:h2:tcp://localhost:9192/test", user, password); DriverManager.getConnection("jdbc:h2:tcp://localhost:"+port2+"/test", user, password);
fail("should not be able to connect in standalone mode"); fail("should not be able to connect in standalone mode");
} catch (SQLException e) { } catch (SQLException e) {
assertKnownException(e); assertKnownException(e);
} }
// test regular cluster connection // test a cluster connection
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9191,localhost:9192/test", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://" + serverList + "/test", user, password);
check(conn, len, "'localhost:9191,localhost:9192'"); check(conn, len, "'"+serverList+"'");
conn.close(); conn.close();
// test if only one server is available at the beginning // stop server 2, and test if only one server is available
n2.stop(); n2.stop();
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9191,localhost:9192/test", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://" + serverList + "/test", user, password);
check(conn, len, "''"); check(conn, len, "''");
conn.close(); conn.close();
// disable the cluster // disable the cluster
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9191/test;CLUSTER=''", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:"+port1+"/test;CLUSTER=''", user, password);
conn.close(); conn.close();
n1.stop(); n1.stop();
...@@ -99,11 +172,11 @@ public class TestCluster extends TestBase { ...@@ -99,11 +172,11 @@ public class TestCluster extends TestBase {
DeleteDbFiles.main("-dir", baseDir + "/node2", "-quiet"); DeleteDbFiles.main("-dir", baseDir + "/node2", "-quiet");
CreateCluster.main("-urlSource", urlNode1, "-urlTarget", CreateCluster.main("-urlSource", urlNode1, "-urlTarget",
urlNode2, "-user", user, "-password", password, "-serverList", urlNode2, "-user", user, "-password", password, "-serverList",
"localhost:9191,localhost:9192"); serverList);
n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "9191", "-baseDir", baseDir + "/node1").start(); n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port1, "-baseDir", baseDir + "/node1").start();
n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "9192", "-baseDir", baseDir + "/node2").start(); n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port2, "-baseDir", baseDir + "/node2").start();
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9191,localhost:9192/test", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://" + serverList + "/test", user, password);
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("CREATE TABLE BOTH(ID INT)"); stat.execute("CREATE TABLE BOTH(ID INT)");
...@@ -113,14 +186,14 @@ public class TestCluster extends TestBase { ...@@ -113,14 +186,14 @@ public class TestCluster extends TestBase {
conn.close(); conn.close();
n2.stop(); n2.stop();
n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "9191", "-baseDir", baseDir + "/node1").start(); n1 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port1, "-baseDir", baseDir + "/node1").start();
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9191/test;CLUSTER=''", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:"+port1+"/test;CLUSTER=''", user, password);
check(conn, len, "''"); check(conn, len, "''");
conn.close(); conn.close();
n1.stop(); n1.stop();
n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "9192", "-baseDir", baseDir + "/node2").start(); n2 = org.h2.tools.Server.createTcpServer("-tcpPort", "" + port2, "-baseDir", baseDir + "/node2").start();
conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:9192/test;CLUSTER=''", user, password); conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:" + port2 + "/test;CLUSTER=''", user, password);
check(conn, len, "''"); check(conn, len, "''");
conn.createStatement().execute("SELECT * FROM A"); conn.createStatement().execute("SELECT * FROM A");
conn.close(); conn.close();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论