From 86dc7a4e7eca4c6f75b9663a7cc34f980da40718 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Thu, 1 Oct 2026 10:43:56 +0200 Subject: [PATCH] allow loadbalanced DB --- client/conf/db.properties.in | 20 +++++++----- .../com/cloud/utils/db/TransactionLegacy.java | 25 ++++++++------- .../cloud/utils/db/TransactionLegacyTest.java | 31 +++++++++---------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/client/conf/db.properties.in b/client/conf/db.properties.in index 0f7d2706a427..4bf226b37d7e 100644 --- a/client/conf/db.properties.in +++ b/client/conf/db.properties.in @@ -104,24 +104,28 @@ db.simulator.uri= # High Availability And Cluster Properties +# When enabled, connections use MySQL Connector/J's load-balancing driver (jdbc:mysql:loadbalance) +# with the first host in the list (db..host) always preferred over the replicas, via +# db.ha.loadBalanceStrategy. connectTimeout/socketTimeout bound how long a new connection can hang +# against an unreachable host; loadBalanceBlocklistTimeout is how long a host that just failed is +# skipped before being retried. This only fails read/write traffic over to a replica connection-wise: +# promoting a replica to a writable primary after a real source outage is still a manual DBA step. db.ha.enabled=false db.ha.loadBalanceStrategy=com.cloud.utils.db.StaticStrategy # cloud stack Database db.cloud.replicas=localhost,localhost db.cloud.autoReconnect=true -db.cloud.failOverReadOnly=false db.cloud.reconnectAtTxEnd=true db.cloud.autoReconnectForPools=true -db.cloud.secondsBeforeRetrySource=3600 -db.cloud.queriesBeforeRetrySource=5000 -db.cloud.initialTimeout=3600 +db.cloud.connectTimeout=3000 +db.cloud.socketTimeout=30000 +db.cloud.loadBalanceBlocklistTimeout=30000 #usage Database db.usage.replicas=localhost,localhost db.usage.autoReconnect=true -db.usage.failOverReadOnly=false db.usage.reconnectAtTxEnd=true db.usage.autoReconnectForPools=true -db.usage.secondsBeforeRetrySource=3600 -db.usage.queriesBeforeRetrySource=5000 -db.usage.initialTimeout=3600 +db.usage.connectTimeout=3000 +db.usage.socketTimeout=30000 +db.usage.loadBalanceBlocklistTimeout=30000 diff --git a/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java b/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java index 18a90749e49c..f27995a98e7b 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java +++ b/framework/db/src/main/java/com/cloud/utils/db/TransactionLegacy.java @@ -1191,8 +1191,8 @@ protected static Pair getConnectionUriAndDriver(Properties dbPro connectionUri = getPropertiesAndBuildConnectionUri(dbProps, loadBalanceStrategy, driver, useSSL, schema); } else { LOGGER.warn(String.format("db.%s.uri was set, ignoring the following properties for schema %s of db.properties: [host, port, name, driver, autoReconnect, url.params," - + " replicas, ha.loadBalanceStrategy, ha.enable, failOverReadOnly, reconnectAtTxEnd, autoReconnectForPools, secondsBeforeRetrySource, queriesBeforeRetrySource, " - + "initialTimeout].", schema, schema)); + + " replicas, ha.loadBalanceStrategy, ha.enable, reconnectAtTxEnd, autoReconnectForPools, connectTimeout, socketTimeout, " + + "loadBalanceBlocklistTimeout].", schema, schema)); String[] splitUri = propertyUri.split(":"); driver = String.format("%s:%s", splitUri[0], splitUri[1]); @@ -1213,19 +1213,22 @@ protected static String getPropertiesAndBuildConnectionUri(Properties dbProps, S String replicas = null; String dbHaParams = null; if (s_dbHAEnabled) { - dbHaParams = getDBHAParams(schema, dbProps); + dbHaParams = getDBHAParams(schema, dbProps, loadBalanceStrategy); replicas = dbProps.getProperty(String.format("db.%s.replicas", schema)); LOGGER.info(String.format("The replicas configured for %s data base are %s.", schema, replicas)); } - return buildConnectionUri(loadBalanceStrategy, driver, useSSL, host, replicas, port, dbName, autoReconnect, urlParams, dbHaParams); + return buildConnectionUri(driver, useSSL, host, replicas, port, dbName, autoReconnect, urlParams, dbHaParams); } - protected static String buildConnectionUri(String loadBalanceStrategy, String driver, boolean useSSL, String host, String replicas, int port, String dbName, boolean autoReconnect, + protected static String buildConnectionUri(String driver, boolean useSSL, String host, String replicas, int port, String dbName, boolean autoReconnect, String urlParams, String dbHaParams) { StringBuilder connectionUri = new StringBuilder(); connectionUri.append(driver); + if (s_dbHAEnabled) { + connectionUri.append(":loadbalance"); + } connectionUri.append("://"); connectionUri.append(host); @@ -1253,8 +1256,6 @@ protected static String buildConnectionUri(String loadBalanceStrategy, String dr if (s_dbHAEnabled) { connectionUri.append("&"); connectionUri.append(dbHaParams); - connectionUri.append("&loadBalanceStrategy="); - connectionUri.append(loadBalanceStrategy); } connectionUri.append("&"); @@ -1409,14 +1410,14 @@ private static DataSource getDefaultDbcpDataSource(final String database) { return new PoolingDataSource(connectionPool); } - private static String getDBHAParams(String dbName, Properties dbProps) { + private static String getDBHAParams(String dbName, Properties dbProps, String loadBalanceStrategy) { StringBuilder sb = new StringBuilder(); - sb.append("failOverReadOnly=" + dbProps.getProperty("db." + dbName + ".failOverReadOnly")); + sb.append("ha.loadBalanceStrategy=").append(loadBalanceStrategy); sb.append("&").append("reconnectAtTxEnd=" + dbProps.getProperty("db." + dbName + ".reconnectAtTxEnd")); sb.append("&").append("autoReconnectForPools=" + dbProps.getProperty("db." + dbName + ".autoReconnectForPools")); - sb.append("&").append("secondsBeforeRetrySource=" + dbProps.getProperty("db." + dbName + ".secondsBeforeRetrySource")); - sb.append("&").append("queriesBeforeRetrySource=" + dbProps.getProperty("db." + dbName + ".queriesBeforeRetrySource")); - sb.append("&").append("initialTimeout=" + dbProps.getProperty("db." + dbName + ".initialTimeout")); + sb.append("&").append("connectTimeout=" + dbProps.getProperty("db." + dbName + ".connectTimeout")); + sb.append("&").append("socketTimeout=" + dbProps.getProperty("db." + dbName + ".socketTimeout")); + sb.append("&").append("loadBalanceBlocklistTimeout=" + dbProps.getProperty("db." + dbName + ".loadBalanceBlocklistTimeout")); return sb.toString(); } diff --git a/framework/db/src/test/java/com/cloud/utils/db/TransactionLegacyTest.java b/framework/db/src/test/java/com/cloud/utils/db/TransactionLegacyTest.java index 2e0af6fa1866..91cec2753d1c 100644 --- a/framework/db/src/test/java/com/cloud/utils/db/TransactionLegacyTest.java +++ b/framework/db/src/test/java/com/cloud/utils/db/TransactionLegacyTest.java @@ -47,7 +47,7 @@ public void getConnectionUriAndDriverTestWithoutUri() { Pair result = TransactionLegacy.getConnectionUriAndDriver(properties, null, false, "cloud"); - Assert.assertEquals("driver://host:5555/name?autoReconnect=false&someParams", result.first()); + Assert.assertEquals("driver://host:5555/name?autoReconnect=false&someParams&scrollTolerantForwardOnly=true", result.first()); Assert.assertEquals("driver", result.second()); } @@ -65,53 +65,52 @@ public void getConnectionUriAndDriverTestWithUri() { public void getPropertiesAndBuildConnectionUriTestDbHaDisabled() { String result = TransactionLegacy.getPropertiesAndBuildConnectionUri(properties, "strat", "driver", true, "cloud"); - Assert.assertEquals("driver://host:5555/name?autoReconnect=false&someParams&useSSL=true", result); + Assert.assertEquals("driver://host:5555/name?autoReconnect=false&someParams&useSSL=true&scrollTolerantForwardOnly=true", result); } @Test public void getPropertiesAndBuildConnectionUriTestDbHaEnabled() { TransactionLegacy.s_dbHAEnabled = true; - properties.setProperty("db.cloud.failOverReadOnly", "true"); properties.setProperty("db.cloud.reconnectAtTxEnd", "false"); properties.setProperty("db.cloud.autoReconnectForPools", "true"); - properties.setProperty("db.cloud.secondsBeforeRetrySource", "25"); - properties.setProperty("db.cloud.queriesBeforeRetrySource", "105"); - properties.setProperty("db.cloud.initialTimeout", "1000"); + properties.setProperty("db.cloud.connectTimeout", "3000"); + properties.setProperty("db.cloud.socketTimeout", "15000"); + properties.setProperty("db.cloud.loadBalanceBlocklistTimeout", "30000"); properties.setProperty("db.cloud.replicas", "second_host"); String result = TransactionLegacy.getPropertiesAndBuildConnectionUri(properties, "strat", "driver", true, "cloud"); - Assert.assertEquals("driver://host,second_host:5555/name?autoReconnect=false&someParams&useSSL=true&failOverReadOnly=true&reconnectAtTxEnd=false&autoReconnectFor" - + "Pools=true&secondsBeforeRetrySource=25&queriesBeforeRetrySource=105&initialTimeout=1000&loadBalanceStrategy=strat", result); + Assert.assertEquals("driver:loadbalance://host,second_host:5555/name?autoReconnect=false&someParams&useSSL=true&ha.loadBalanceStrategy=strat&reconnectAtTxEnd=false&autoReconnectFor" + + "Pools=true&connectTimeout=3000&socketTimeout=15000&loadBalanceBlocklistTimeout=30000&scrollTolerantForwardOnly=true", result); } @Test public void buildConnectionUriTestDbHaDisabled() { - String result = TransactionLegacy.buildConnectionUri(null, "driver", false, "host", null, 5555, "cloud", false, null, null); + String result = TransactionLegacy.buildConnectionUri("driver", false, "host", null, 5555, "cloud", false, null, null); - Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false", result); + Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false&scrollTolerantForwardOnly=true", result); } @Test public void buildConnectionUriTestDbHaEnabled() { TransactionLegacy.s_dbHAEnabled = true; - String result = TransactionLegacy.buildConnectionUri("strat", "driver", false, "host", "second_host", 5555, "cloud", false, null, "dbHaParams"); + String result = TransactionLegacy.buildConnectionUri("driver", false, "host", "second_host", 5555, "cloud", false, null, "dbHaParams"); - Assert.assertEquals("driver://host,second_host:5555/cloud?autoReconnect=false&dbHaParams&loadBalanceStrategy=strat", result); + Assert.assertEquals("driver:loadbalance://host,second_host:5555/cloud?autoReconnect=false&dbHaParams&scrollTolerantForwardOnly=true", result); } @Test public void buildConnectionUriTestUrlParamsNotNull() { - String result = TransactionLegacy.buildConnectionUri(null, "driver", false, "host", null, 5555, "cloud", false, "urlParams", null); + String result = TransactionLegacy.buildConnectionUri("driver", false, "host", null, 5555, "cloud", false, "urlParams", null); - Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false&urlParams", result); + Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false&urlParams&scrollTolerantForwardOnly=true", result); } @Test public void buildConnectionUriTestUseSslTrue() { - String result = TransactionLegacy.buildConnectionUri(null, "driver", true, "host", null, 5555, "cloud", false, null, null); + String result = TransactionLegacy.buildConnectionUri("driver", true, "host", null, 5555, "cloud", false, null, null); - Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false&useSSL=true", result); + Assert.assertEquals("driver://host:5555/cloud?autoReconnect=false&useSSL=true&scrollTolerantForwardOnly=true", result); } }