mirror of
https://github.com/keycloak/keycloak.git
synced 2026-06-11 10:00:06 -04:00
Added implementation for setting a default connection timeout for all databases types
Closes #46809 Signed-off-by: Ruchika <ruchika.jha1@ibm.com> Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com> Co-authored-by: Alexander Schwartz <alexander.schwartz@ibm.com>
This commit is contained in:
parent
be0da0392b
commit
efa2df641c
5 changed files with 243 additions and 4 deletions
|
|
@ -171,7 +171,8 @@ This behavior is enabled by default and can be controlled with the server option
|
|||
|
||||
* `--truststore-kubernetes-enabled=true|false` (default: `true`)
|
||||
|
||||
No changes are required for most deployments. If you previously relied on the Operator to manage these truststore entries, the server now performs the same function directly.
|
||||
No changes are required for most deployments.
|
||||
If you previously relied on the Operator to manage these truststore entries, the server now performs the same function directly.
|
||||
|
||||
WARNING: when `https-client-auth` is set to `required` or `request` without an explicit `https-trust-store-file`, mTLS client certificate validation falls back to the system truststore. With `truststore-kubernetes-enabled=true`, this means certificates signed by the Kubernetes cluster CA will be accepted as valid client certificates. If this is not desired, either set `https-trust-store-file` explicitly or disable `truststore-kubernetes-enabled`.
|
||||
|
||||
|
|
@ -259,6 +260,11 @@ As part of this change, the database configuration documentation has been update
|
|||
The previously documented `utf8mb3` (or `utf8`) character set has been removed from the documentation due to its limitations in storing certain Unicode characters.
|
||||
* Previously recommended JDBC driver settings for Oracle, MySQL, and MariaDB have been removed from the documentation, as current versions of these databases use appropriate default values.
|
||||
|
||||
=== Automatic database connection timeout defaults
|
||||
|
||||
To improve failover behavior and startup resilience during network issues, {project_name} now sets a default database connection timeout of 10 seconds.
|
||||
See https://www.keycloak.org/server/db[Configuring the database] for the list of databases, and on how to change this default.
|
||||
|
||||
// ------------------------ Deprecated features ------------------------ //
|
||||
== Deprecated features
|
||||
|
||||
|
|
|
|||
|
|
@ -281,6 +281,78 @@ For example:
|
|||
|
||||
<@kc.start parameters="--db mssql --db-url-properties=';sendStringParametersAsUnicode=true'"/>
|
||||
|
||||
== Automatic database connection timeout
|
||||
|
||||
When {project_name} connects to the database, network problems can occur, especially during failovers or switchovers.
|
||||
To improve resilience and ensure faster recovery, {project_name} automatically sets a default connection timeout of 10 seconds for selected database vendors when using the standard JDBC driver.
|
||||
|
||||
The following table lists the affected vendors, the JDBC driver property used, and the default value applied by {project_name}:
|
||||
|
||||
[%autowidth]
|
||||
|===
|
||||
|Database |JDBC driver property |Default value |Unit
|
||||
|
||||
|MySQL
|
||||
|`connectTimeout`
|
||||
|`10000`
|
||||
|milliseconds
|
||||
|
||||
|MariaDB
|
||||
|`connectTimeout`
|
||||
|`10000`
|
||||
|milliseconds
|
||||
|
||||
|PostgreSQL
|
||||
|`connectTimeout`
|
||||
|`10`
|
||||
|seconds
|
||||
|
||||
|Oracle Database
|
||||
|`oracle.net.CONNECT_TIMEOUT`
|
||||
|`10000`
|
||||
|milliseconds
|
||||
|
||||
|Microsoft SQL Server
|
||||
|`loginTimeout`
|
||||
|`10`
|
||||
|seconds
|
||||
|
||||
|===
|
||||
|
||||
{project_name} applies these defaults automatically, but only when all of the following conditions are met:
|
||||
|
||||
* The database vendor is configured via `--db`.
|
||||
* {project_name} is using the standard JDBC driver for that vendor.
|
||||
* The timeout property has not already been set explicitly by the user in `db-url` or `db-url-properties`.
|
||||
|
||||
=== Overriding the default connection timeout
|
||||
|
||||
To use a different connection timeout, set the relevant JDBC driver property explicitly via `db-url` or `db-url-properties`.
|
||||
|
||||
For MySQL:
|
||||
|
||||
<@kc.start parameters="--db mysql --db-url-properties='?connectTimeout=30000'"/>
|
||||
|
||||
For MariaDB:
|
||||
|
||||
<@kc.start parameters="--db mariadb --db-url-properties='?connectTimeout=30000'"/>
|
||||
|
||||
For Microsoft SQL Server:
|
||||
|
||||
<@kc.start parameters="--db mssql --db-url-properties=';loginTimeout=20'"/>
|
||||
|
||||
For PostgreSQL:
|
||||
|
||||
<@kc.start parameters="--db postgres --db-url-properties='?connectTimeout=30'"/>
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When using `db-url-properties`, prepend the correct delimiter for your vendor's JDBC URL format:
|
||||
|
||||
* PostgreSQL, MySQL, and MariaDB: use `?` as the first property delimiter, or `&` for subsequent properties.
|
||||
* Microsoft SQL Server: use `;` as the property delimiter.
|
||||
====
|
||||
|
||||
== Preparing for PostgreSQL
|
||||
|
||||
=== Writer and reader instances
|
||||
|
|
|
|||
|
|
@ -126,7 +126,36 @@ public class DatabaseOptions {
|
|||
.defaultValue("false")
|
||||
.hidden()
|
||||
.build();
|
||||
|
||||
public static final Option<String> DB_MYSQL_CONNECT_TIMEOUT = new OptionBuilder<>("db-mysql-connect-timeout", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.defaultValue("10000") // 10 seconds in milliseconds
|
||||
.hidden()
|
||||
.build();
|
||||
public static final Option<String> DB_MARIADB_CONNECT_TIMEOUT = new OptionBuilder<>("db-mariadb-connect-timeout", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.defaultValue("10000") // 10 seconds in milliseconds
|
||||
.hidden()
|
||||
.build();
|
||||
public static final Option<String> DB_ORACLE_CONNECT_TIMEOUT = new OptionBuilder<>("db-oracle-connect-timeout", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.defaultValue("10000") // 10 seconds in milliseconds
|
||||
.hidden()
|
||||
.build();
|
||||
public static final Option<String> DB_MSSQL_CONNECT_TIMEOUT = new OptionBuilder<>("db-mssql-login-timeout", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.defaultValue("10") // 10 seconds, unit is SECONDS
|
||||
.hidden()
|
||||
.build();
|
||||
public static final Option<String> DB_POSTGRES_CONNECT_TIMEOUT = new OptionBuilder<>("db-postgres-connect-timeout", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.defaultValue("10") // 10 seconds, unit is SECONDS
|
||||
.hidden()
|
||||
.build();
|
||||
public static final Option<String> DB_TIDB_CONNECT_TIMEOUT = new OptionBuilder<>("db-tidb-connect-timeout", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.defaultValue("10000") // 10 seconds in milliseconds
|
||||
.hidden()
|
||||
.build();
|
||||
public static final class Datasources {
|
||||
/**
|
||||
* Options that have their sibling for a named datasource
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.
|
|||
public final class DatabasePropertyMappers implements PropertyMapperGrouping {
|
||||
public static final String PG_TARGET_SERVER_TYPE = "quarkus.datasource.jdbc.additional-jdbc-properties.targetServerType";
|
||||
public static final String MSSQL_SEND_STRING_PARAMETER_AS_UNICODE = "quarkus.datasource.jdbc.additional-jdbc-properties.sendStringParametersAsUnicode";
|
||||
public static final String MYSQL_CONNECT_TIMEOUT = "quarkus.datasource.jdbc.additional-jdbc-properties.connectTimeout";
|
||||
public static final String MARIADB_CONNECT_TIMEOUT = "quarkus.datasource.jdbc.additional-jdbc-properties.connectTimeout";
|
||||
public static final String ORACLEDB_CONNECT_TIMEOUT = "quarkus.datasource.jdbc.additional-jdbc-properties.oracle.net.CONNECT_TIMEOUT";
|
||||
public static final String MSSQL_CONNECT_TIMEOUT = "quarkus.datasource.jdbc.additional-jdbc-properties.loginTimeout";
|
||||
private static final String POSTGRES_CONNECT_TIMEOUT = "quarkus.datasource.jdbc.additional-jdbc-properties.connectTimeout";
|
||||
private static final String TIDB_CONNECT_TIMEOUT = "quarkus.datasource.jdbc.additional-jdbc-properties.connectTimeout";
|
||||
|
||||
private static final Logger log = Logger.getLogger(DatabasePropertyMappers.class);
|
||||
|
||||
/**
|
||||
|
|
@ -76,6 +83,30 @@ public final class DatabasePropertyMappers implements PropertyMapperGrouping {
|
|||
.to(MSSQL_SEND_STRING_PARAMETER_AS_UNICODE)
|
||||
.isEnabled(() -> isMssqlSendStringParametersAsUnicode())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_MYSQL_CONNECT_TIMEOUT)
|
||||
.to(MYSQL_CONNECT_TIMEOUT)
|
||||
.isEnabled(() -> isMysqlConnectTimeoutEnabled())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_MARIADB_CONNECT_TIMEOUT)
|
||||
.to(MARIADB_CONNECT_TIMEOUT)
|
||||
.isEnabled(() -> isMariadbConnectTimeoutEnabled())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_ORACLE_CONNECT_TIMEOUT)
|
||||
.to(ORACLEDB_CONNECT_TIMEOUT)
|
||||
.isEnabled(() -> isOracleConnectTimeoutEnabled())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_MSSQL_CONNECT_TIMEOUT)
|
||||
.to(MSSQL_CONNECT_TIMEOUT)
|
||||
.isEnabled(() -> isMssqlLoginTimeoutEnabled())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_POSTGRES_CONNECT_TIMEOUT)
|
||||
.to(POSTGRES_CONNECT_TIMEOUT)
|
||||
.isEnabled(() -> isPostgresConnectTimeoutEnabled())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_TIDB_CONNECT_TIMEOUT)
|
||||
.to(TIDB_CONNECT_TIMEOUT)
|
||||
.isEnabled(() -> isTidbConnectTimeoutEnabled())
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_URL_HOST)
|
||||
.paramLabel("hostname")
|
||||
.build(),
|
||||
|
|
@ -196,14 +227,58 @@ public final class DatabasePropertyMappers implements PropertyMapperGrouping {
|
|||
|
||||
if (!Objects.equals(Database.getDriver(db, true).orElse(null), dbDriver) &&
|
||||
!Objects.equals(Database.getDriver(db, false).orElse(null), dbDriver)) {
|
||||
// Custom JDBC-Driver, for example, AWS JDBC Wrapper.
|
||||
return false;
|
||||
}
|
||||
|
||||
// sendStringParametersAsUnicode already set by user in db-url or db-url-properties, ignore
|
||||
return !dbUrl.contains("sendStringParametersAsUnicode") &&
|
||||
!dbUrlProperties.contains("sendStringParametersAsUnicode");
|
||||
}
|
||||
|
||||
public static boolean isMysqlConnectTimeoutEnabled() {
|
||||
return isConnectTimeoutEnabled(Database.Vendor.MYSQL, "connectTimeout");
|
||||
}
|
||||
|
||||
public static boolean isMariadbConnectTimeoutEnabled() {
|
||||
return isConnectTimeoutEnabled(Database.Vendor.MARIADB, "connectTimeout");
|
||||
}
|
||||
|
||||
public static boolean isOracleConnectTimeoutEnabled() {
|
||||
return isConnectTimeoutEnabled(Database.Vendor.ORACLE, "oracle.net.CONNECT_TIMEOUT");
|
||||
}
|
||||
|
||||
public static boolean isMssqlLoginTimeoutEnabled() {
|
||||
return isConnectTimeoutEnabled(Database.Vendor.MSSQL, "loginTimeout");
|
||||
}
|
||||
|
||||
public static boolean isPostgresConnectTimeoutEnabled() {
|
||||
return isConnectTimeoutEnabled(Database.Vendor.POSTGRES, "connectTimeout");
|
||||
}
|
||||
|
||||
public static boolean isTidbConnectTimeoutEnabled() {
|
||||
return isConnectTimeoutEnabled(Database.Vendor.TIDB, "connectTimeout");
|
||||
}
|
||||
|
||||
private static boolean isConnectTimeoutEnabled(Database.Vendor expectedVendor, String timeoutProperty) {
|
||||
String db = Configuration.getConfigValue(DB).getValue();
|
||||
Database.Vendor vendor = Database.getVendor(db).orElse(null);
|
||||
if (vendor != expectedVendor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String dbDriver = Configuration.getConfigValue(DatabaseOptions.DB_DRIVER).getValue();
|
||||
if (!Objects.equals(Database.getDriver(db, true).orElse(null), dbDriver) &&
|
||||
!Objects.equals(Database.getDriver(db, false).orElse(null), dbDriver)) {
|
||||
// Custom JDBC driver (e.g. AWS JDBC Wrapper) — do not inject defaults
|
||||
return false;
|
||||
}
|
||||
|
||||
String dbUrl = Configuration.getConfigValue(DatabaseOptions.DB_URL).getValueOrDefault("");
|
||||
String dbUrlProperties = Configuration.getKcConfigValue(DatabaseOptions.DB_URL_PROPERTIES.getKey()).getValueOrDefault("");
|
||||
|
||||
// Property already set explicitly by the user — do not override
|
||||
return !dbUrl.contains(timeoutProperty) && !dbUrlProperties.contains(timeoutProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting with H2 version 2.x, marking "VALUE" as a non-keyword is necessary as some columns are named "VALUE" in the Keycloak schema.
|
||||
* <p />
|
||||
|
|
|
|||
|
|
@ -712,4 +712,61 @@ public class ConfigurationTest extends AbstractConfigurationTest {
|
|||
private static Config.Scope cacheEmbeddedConfiguration() {
|
||||
return initConfig(CacheEmbeddedConfigProviderSpi.SPI_NAME, DefaultCacheEmbeddedConfigProviderFactory.PROVIDER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultDatabaseConnectTimeouts() {
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mysql");
|
||||
SmallRyeConfig config = createConfig();
|
||||
assertTrue(DatabasePropertyMappers.isMysqlConnectTimeoutEnabled());
|
||||
assertEquals("10000", config.getConfigValue(DatabasePropertyMappers.MYSQL_CONNECT_TIMEOUT).getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mysql", "--db-url-properties=?connectTimeout=5000");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isMysqlConnectTimeoutEnabled());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mysql", "--db-url=jdbc:mysql://localhost:3306/keycloak?connectTimeout=5000");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isMysqlConnectTimeoutEnabled());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mariadb");
|
||||
config = createConfig();
|
||||
assertTrue(DatabasePropertyMappers.isMariadbConnectTimeoutEnabled());
|
||||
assertEquals("10000", config.getConfigValue(DatabasePropertyMappers.MARIADB_CONNECT_TIMEOUT).getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mariadb", "--db-url=jdbc:mariadb://localhost:3306/keycloak?connectTimeout=5000");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isMariadbConnectTimeoutEnabled());
|
||||
|
||||
// MariaDB: connectTimeout
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mariadb", "--db-url-properties=?connectTimeout=5000");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isMariadbConnectTimeoutEnabled());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=oracle");
|
||||
config = createConfig();
|
||||
assertTrue(DatabasePropertyMappers.isOracleConnectTimeoutEnabled());
|
||||
assertEquals("10000", config.getConfigValue(DatabasePropertyMappers.ORACLEDB_CONNECT_TIMEOUT).getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=oracle", "--db-url-properties=?oracle.net.CONNECT_TIMEOUT=5000");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isOracleConnectTimeoutEnabled());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=oracle", "--db-url=jdbc:oracle:thin:@//localhost:1521/keycloak?oracle.net.CONNECT_TIMEOUT=5000");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isOracleConnectTimeoutEnabled());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mssql");
|
||||
config = createConfig();
|
||||
assertTrue(DatabasePropertyMappers.isMssqlLoginTimeoutEnabled());
|
||||
assertEquals("10", config.getConfigValue(DatabasePropertyMappers.MSSQL_CONNECT_TIMEOUT).getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mssql", "--db-url-properties=;loginTimeout=20");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isMssqlLoginTimeoutEnabled());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=mssql", "--db-url=jdbc:sqlserver://localhost:1433;databaseName=keycloak;loginTimeout=20");
|
||||
config = createConfig();
|
||||
assertFalse(DatabasePropertyMappers.isMssqlLoginTimeoutEnabled());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue