Keep at least one DataSource in the pool when update by events.

This commit is contained in:
DigitalSonic
2020-05-05 22:21:00 +08:00
parent 75391df4ad
commit 99270819ee
5 changed files with 65 additions and 6 deletions
@@ -97,6 +97,7 @@ public class HighAvailableDataSource extends WrapperAdapter implements DataSourc
private String dataSourceFile = DEFAULT_DATA_SOURCE_FILE;
private String propertyPrefix = "";
private int poolPurgeIntervalSeconds = PoolUpdater.DEFAULT_INTERVAL;
private boolean allowEmptyPoolWhenUpdate = false;
private volatile boolean inited = false;
@@ -113,6 +114,7 @@ public class HighAvailableDataSource extends WrapperAdapter implements DataSourc
}
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
poolUpdater.setIntervalSeconds(poolPurgeIntervalSeconds);
poolUpdater.setAllowEmptyPool(allowEmptyPoolWhenUpdate);
poolUpdater.init();
createNodeMap();
}
@@ -304,6 +306,14 @@ public class HighAvailableDataSource extends WrapperAdapter implements DataSourc
nodeListener.update(); // Do update in the current Thread at the startup
}
public boolean isAllowEmptyPoolWhenUpdate() {
return allowEmptyPoolWhenUpdate;
}
public void setAllowEmptyPoolWhenUpdate(boolean allowEmptyPoolWhenUpdate) {
this.allowEmptyPoolWhenUpdate = allowEmptyPoolWhenUpdate;
}
// Getters & Setters
public int getPoolPurgeIntervalSeconds() {
@@ -50,6 +50,7 @@ public class PoolUpdater implements Observer {
private ScheduledExecutorService executor;
private int intervalSeconds = DEFAULT_INTERVAL;
private volatile boolean inited = false;
private boolean allowEmptyPool = false;
public PoolUpdater(HighAvailableDataSource highAvailableDataSource) {
setHighAvailableDataSource(highAvailableDataSource);
@@ -213,6 +214,11 @@ public class PoolUpdater implements Observer {
if (nodeName == null || nodeName.isEmpty() || !map.containsKey(nodeName)) {
return;
}
map = highAvailableDataSource.getAvailableDataSourceMap();
if (!allowEmptyPool && map.size() == 1 && map.containsKey(nodeName)) {
LOG.warn(nodeName + " is the only DataSource left, don't remove it.");
return;
}
blacklistNode(nodeName);
}
@@ -247,4 +253,12 @@ public class PoolUpdater implements Observer {
public void setIntervalSeconds(int intervalSeconds) {
this.intervalSeconds = intervalSeconds;
}
public boolean isAllowEmptyPool() {
return allowEmptyPool;
}
public void setAllowEmptyPool(boolean allowEmptyPool) {
this.allowEmptyPool = allowEmptyPool;
}
}
@@ -55,11 +55,11 @@ public class FileNodeListenerTest {
DruidDataSource foo = (DruidDataSource) dataSource.getAvailableDataSourceMap().get("foo");
assertEquals("jdbc:derby:memory:foo;create=true", foo.getUrl());
// Remove all
// try to emove all but we have to keep one left
writePropertiesFile(file, new Properties());
Thread.sleep(6000);
assertTrue(dataSource.getAvailableDataSourceMap().isEmpty());
assertEquals(1, dataSource.getAvailableDataSourceMap().size());
dataSource.destroy();
}
@@ -39,6 +39,7 @@ public class PoolUpdaterTest {
@Test
public void testUpdate() {
haDataSource.getDataSourceMap().put("foo", new MockDataSource("foo"));
haDataSource.getDataSourceMap().put("bar", new MockDataSource("bar"));
NodeEvent event = new NodeEvent();
event.setNodeName("foo");
@@ -91,6 +92,18 @@ public class PoolUpdaterTest {
assertTrue(haDataSource.getDataSourceMap().isEmpty());
}
@Test
public void testUpdate_onlyOneLeftToRemove() {
haDataSource.getDataSourceMap().put("foo", new MockDataSource("foo"));
NodeEvent event = new NodeEvent();
event.setNodeName("foo");
event.setType(NodeEventTypeEnum.DELETE);
updater.update(new FileNodeListener(), new NodeEvent[] { event });
assertTrue(haDataSource.getDataSourceMap().containsKey("foo"));
}
@Test
public void testAddNode() {
String url = "jdbc:derby:memory:foo;create=true";
@@ -127,6 +140,7 @@ public class PoolUpdaterTest {
@Test
public void testDeleteNode() {
haDataSource.getDataSourceMap().put("foo", new MockDataSource("foo"));
haDataSource.getDataSourceMap().put("bar", new MockDataSource("bar"));
NodeEvent event = new NodeEvent();
event.setNodeName("foo");
@@ -56,10 +56,11 @@ public class ZookeeperNodeListenerTest {
// 2. Register one Node
ZookeeperNodeRegister register = registerNodeAndReturnRegister();
ZookeeperNodeRegister register2 = registerAnotherNodeAndReturnRegister();
Thread.sleep(3000); // Wait for the Node to be created.
assertFalse(dataSource.getDataSourceMap().isEmpty());
assertEquals(1, dataSource.getAvailableDataSourceMap().size());
assertEquals(2, dataSource.getAvailableDataSourceMap().size());
DruidDataSource ds = (DruidDataSource) dataSource.getAvailableDataSourceMap().get("foo.test-foo");
assertEquals("jdbc:derby:memory:foo;create=true", ds.getUrl());
assertNotNull(dataSource.getConnection());
@@ -70,15 +71,16 @@ public class ZookeeperNodeListenerTest {
for (int i = 0; i < 10; i++) {
Thread.sleep(500);
if (dataSource.isInBlackList("foo.test-foo")) {
assertTrue(dataSource.getAvailableDataSourceMap().isEmpty());
assertEquals(1, dataSource.getAvailableDataSourceMap().size());
assertFalse(dataSource.getDataSourceMap().isEmpty());
assertNull(dataSource.getConnection());
assertNotNull(dataSource.getConnection());
checkBlackList = true;
break;
}
}
assertTrue(checkBlackList);
register2.destroy();
Thread.sleep(3000); // Wait for the destory to be finished
dataSource.destroy();
}
@@ -187,6 +189,25 @@ public class ZookeeperNodeListenerTest {
return register;
}
private ZookeeperNodeRegister registerAnotherNodeAndReturnRegister() {
ZookeeperNodeRegister register = new ZookeeperNodeRegister();
register.setZkConnectString(server.getConnectString());
register.setPath(PATH);
register.init();
List<ZookeeperNodeInfo> payload = new ArrayList<ZookeeperNodeInfo>();
ZookeeperNodeInfo node = new ZookeeperNodeInfo();
node.setPrefix("bar");
node.setHost("127.0.0.1");
node.setPort(5678);
node.setDatabase("bar");
node.setUsername("sa");
node.setPassword("");
payload.add(node);
register.register("test-bar", payload);
return register;
}
@Test
public void testInitFailure() {
ZookeeperNodeListener listener = new ZookeeperNodeListener();