mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Automatically connect to a writer instance of PostgreSQL (#40384)
Closes #40383 Signed-off-by: Alexander Schwartz <alexander.schwartz@gmx.net> Co-authored-by: Václav Muzikář <vaclav@muzikari.cz> Co-authored-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
parent
47ca339656
commit
05d0c34681
@ -1,3 +1,4 @@
|
||||
// ------------------------ Breaking changes ------------------------ //
|
||||
== Breaking changes
|
||||
|
||||
Breaking changes are identified as requiring changes from existing users to their configurations.
|
||||
@ -5,6 +6,7 @@ In minor or patch releases we will only do breaking changes to fix bugs.
|
||||
|
||||
=== <TODO>
|
||||
|
||||
// ------------------------ Notable changes ------------------------ //
|
||||
== Notable changes
|
||||
|
||||
Notable changes where an internal behavior changed to prevent common misconfigurations, fix bugs or simplify running {project_name}.
|
||||
@ -23,14 +25,25 @@ GET /admin/realms/{realm}/users?exact=false&q=myattribute:
|
||||
|
||||
The {project_name} Admin Client is also updated with a new method to search users by attribute using the `exact` request parameter.
|
||||
|
||||
=== Automatic database connection properties for the PostgreSQL driver
|
||||
|
||||
When running PostgreSQL reader and writer instances, {project_name} needs to always connect to the writer instance to do its work.
|
||||
|
||||
Starting with this release, and when using the original PostgreSQL driver, {project_name} sets the `targetServerType` property of the PostgreSQL JDBC driver to `primary` to ensure that it always connects to a writable primary instance and never connects to a secondary reader instance in failover or switchover scenarios.
|
||||
|
||||
You can override this behavior by setting your own value for `targetServerType` in the DB URL or additional properties.
|
||||
|
||||
// ------------------------ Deprecated features ------------------------ //
|
||||
== Deprecated features
|
||||
|
||||
The following sections provide details on deprecated features.
|
||||
|
||||
=== <TODO>
|
||||
|
||||
// ------------------------ Removed features ------------------------ //
|
||||
== Removed features
|
||||
|
||||
The following features have been removed from this release.
|
||||
|
||||
=== <TODO>
|
||||
|
||||
|
||||
@ -271,6 +271,18 @@ show server_encoding;
|
||||
create database keycloak with encoding 'UTF8';
|
||||
----
|
||||
|
||||
== Preparing for PostgreSQL
|
||||
|
||||
When running PostgreSQL reader and writer instances, {project_name} needs to always connect to the writer instance to do its work.
|
||||
When using the original PostgreSQL driver, {project_name} sets the `targetServerType` property of the PostgreSQL JDBC driver to `primary` to ensure that it always connects to a writable primary instance and never connects to a secondary reader instance in failover or switchover scenarios.
|
||||
|
||||
You can override this behavior by setting your own value for `targetServerType` in the DB URL or additional properties.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The `targetServerType` is only applied automatically to the primary datasource, as requirements might be different for additional datasources.
|
||||
====
|
||||
|
||||
[[preparing-keycloak-for-amazon-aurora-postgresql]]
|
||||
== Preparing for Amazon Aurora PostgreSQL
|
||||
|
||||
@ -296,6 +308,8 @@ See the <@links.server id="containers" /> {section} for details on how to build
|
||||
`db-url`:: Insert `aws-wrapper` to the regular PostgreSQL JDBC URL resulting in a URL like `+jdbc:aws-wrapper:postgresql://...+`.
|
||||
`db-driver`:: Set to `software.amazon.jdbc.Driver` to use the AWS JDBC wrapper.
|
||||
|
||||
NOTE: When overriding the `wrapperPlugins` option of the AWS JDBC Driver, always include the `failover` or `failover2` plugin to ensure that {project_name} always connects to the writer instance even in failover or switchover scenarios.
|
||||
|
||||
== Preparing for MySQL server
|
||||
|
||||
Beginning with MySQL 8.0.30, MySQL supports generated invisible primary keys for any InnoDB table that is created without an explicit primary key (more information https://dev.mysql.com/doc/refman/8.0/en/create-table-gipks.html[here]).
|
||||
|
||||
@ -106,6 +106,11 @@ public class DatabaseOptions {
|
||||
.description("Deactivate specific named datasource <datasource>.")
|
||||
.build();
|
||||
|
||||
public static final Option<String> DB_POSTGRESQL_TARGET_SERVER_TYPE = new OptionBuilder<>("db-postgres-target-server-type", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.hidden()
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Options that have their sibling for a named datasource
|
||||
* Example: for `db-dialect`, `db-dialect-<datasource>` is created
|
||||
|
||||
@ -15,10 +15,12 @@ import org.keycloak.utils.StringUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.keycloak.config.DatabaseOptions.DB;
|
||||
import static org.keycloak.config.DatabaseOptions.OPTIONS_DATASOURCES;
|
||||
import static org.keycloak.config.DatabaseOptions.getDatasourceOption;
|
||||
import static org.keycloak.config.DatabaseOptions.getKeyForDatasource;
|
||||
@ -51,6 +53,11 @@ final class DatabasePropertyMappers {
|
||||
.mapFrom(DatabaseOptions.DB, DatabasePropertyMappers::getDatabaseUrl)
|
||||
.paramLabel("jdbc-url")
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_POSTGRESQL_TARGET_SERVER_TYPE)
|
||||
.to("quarkus.datasource.jdbc.additional-jdbc-properties.targetServerType")
|
||||
.mapFrom(DatabaseOptions.DB, DatabasePropertyMappers::getPostgresqlTargetServerType)
|
||||
.isEnabled(() -> getPostgresqlTargetServerType(Configuration.getConfigValue(DB).getValue(), null) != null)
|
||||
.build(),
|
||||
fromOption(DatabaseOptions.DB_URL_HOST)
|
||||
.paramLabel("hostname")
|
||||
.build(),
|
||||
@ -114,6 +121,33 @@ final class DatabasePropertyMappers {
|
||||
.description("Used for internal purposes of H2 database.")
|
||||
.build();
|
||||
|
||||
private static String getPostgresqlTargetServerType(String db, ConfigSourceInterceptorContext context) {
|
||||
Database.Vendor vendor = Database.getVendor(db).orElse(null);
|
||||
if (vendor != Database.Vendor.POSTGRES) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String dbDriver = Configuration.getConfigValue(DatabaseOptions.DB_DRIVER).getValue();
|
||||
String dbUrl = Configuration.getConfigValue(DatabaseOptions.DB_URL).getValue();
|
||||
String dbUrlProperties = Configuration.getConfigValue(DatabaseOptions.DB_URL_PROPERTIES).getValue();
|
||||
|
||||
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 null;
|
||||
}
|
||||
if (dbUrlProperties != null && dbUrl != null && dbUrl.contains("${kc.db-url-properties:}") && dbUrlProperties.contains("targetServerType")) {
|
||||
// targetServerType already set to same or different value in db-url-properties, ignore
|
||||
return null;
|
||||
}
|
||||
if (dbUrl != null && dbUrl.contains("targetServerType")) {
|
||||
// targetServerType already set to same or different value in db-url, ignore
|
||||
return null;
|
||||
}
|
||||
log.debug("setting targetServerType for PostgreSQL to 'primary'");
|
||||
return "primary";
|
||||
}
|
||||
|
||||
private static String getDatabaseUrl(String name, String value, ConfigSourceInterceptorContext c) {
|
||||
return Database.getDefaultUrl(name, value).orElse(null);
|
||||
}
|
||||
|
||||
@ -45,7 +45,6 @@ import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.config.CachingOptions;
|
||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.vault.FilesKeystoreVaultProviderFactory;
|
||||
@ -355,6 +354,24 @@ public class ConfigurationTest extends AbstractConfigurationTest {
|
||||
config = createConfig();
|
||||
assertEquals("test-schema", config.getConfigValue("kc.db-schema").getValue());
|
||||
assertEquals("test-schema", config.getConfigValue("kc.db-schema").getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=postgres");
|
||||
config = createConfig();
|
||||
assertEquals("primary", config.getConfigValue("quarkus.datasource.jdbc.additional-jdbc-properties.targetServerType").getValue());
|
||||
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=postgres", "--db-url-properties=?targetServerType=any");
|
||||
config = createConfig();
|
||||
assertNull(config.getConfigValue("quarkus.datasource.jdbc.additional-jdbc-properties.targetServerType").getValue());
|
||||
assertEquals("jdbc:postgresql://localhost:5432/keycloak?targetServerType=any", config.getConfigValue("quarkus.datasource.jdbc.url").getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=postgres", "--db-driver=software.amazon.jdbc.Driver");
|
||||
config = createConfig();
|
||||
assertNull(config.getConfigValue("quarkus.datasource.jdbc.additional-jdbc-properties.targetServerType").getValue());
|
||||
|
||||
ConfigArgsConfigSource.setCliArgs("--db=postgres", "--db-url=jdbc:postgresql://localhost:5432/keycloak?targetServerType=any");
|
||||
config = createConfig();
|
||||
assertNull(config.getConfigValue("quarkus.datasource.jdbc.additional-jdbc-properties.targetServerType").getValue());
|
||||
}
|
||||
|
||||
// KEYCLOAK-15632
|
||||
|
||||
29
tests/base/src/test/java/org/keycloak/tests/db/DbTest.java
Normal file
29
tests/base/src/test/java/org/keycloak/tests/db/DbTest.java
Normal file
@ -0,0 +1,29 @@
|
||||
package org.keycloak.tests.db;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.config.DatabaseOptions;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
|
||||
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class DbTest {
|
||||
|
||||
@InjectRunOnServer
|
||||
RunOnServerClient runOnServer;
|
||||
|
||||
@Test
|
||||
public void ensurePostgreSQLSettingsAreApplied() {
|
||||
runOnServer.run(session -> {
|
||||
if (Configuration.getConfigValue(DatabaseOptions.DB).getValue().equals("postgres") &&
|
||||
Configuration.getConfigValue(DatabaseOptions.DB_DRIVER).getValue().equals("org.postgresql.Driver")) {
|
||||
Assertions.assertEquals("primary", Configuration.getConfigValue(DatabaseOptions.DB_POSTGRESQL_TARGET_SERVER_TYPE).getValue());
|
||||
} else {
|
||||
Assertions.assertNull(Configuration.getConfigValue(DatabaseOptions.DB_POSTGRESQL_TARGET_SERVER_TYPE).getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,6 +4,6 @@ import org.junit.platform.suite.api.SelectPackages;
|
||||
import org.junit.platform.suite.api.Suite;
|
||||
|
||||
@Suite
|
||||
@SelectPackages({"org.keycloak.tests.admin"})
|
||||
@SelectPackages({"org.keycloak.tests.admin", "org.keycloak.tests.db"})
|
||||
public class DatabaseTestSuite {
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user