Additional datasources now require XA (#32403)
* Additional datasources now require XA Closes #32402 Signed-off-by: Václav Muzikář <vmuzikar@redhat.com> * Apply suggestions from code review Co-authored-by: Steven Hawkins <shawkins@redhat.com> Signed-off-by: Václav Muzikář <vaclav@muzikari.cz> * Relax validation Signed-off-by: Václav Muzikář <vmuzikar@redhat.com> * Added a note on recovery Signed-off-by: Václav Muzikář <vmuzikar@redhat.com> * Fix `CustomJpaEntityProviderDistTest` Signed-off-by: Václav Muzikář <vmuzikar@redhat.com> --------- Signed-off-by: Václav Muzikář <vmuzikar@redhat.com> Signed-off-by: Václav Muzikář <vaclav@muzikari.cz> Co-authored-by: Steven Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
2a64caf3fd
commit
7d3dcae96e
5 changed files with 86 additions and 6 deletions
|
@ -190,6 +190,15 @@ Additionally, the following resources have been removed from the `common` theme:
|
||||||
|
|
||||||
If you previously used any of the removed resources in your theme, make sure to add them to your own theme resources instead.
|
If you previously used any of the removed resources in your theme, make sure to add them to your own theme resources instead.
|
||||||
|
|
||||||
|
= Additional datasources now require using XA
|
||||||
|
|
||||||
|
{project_name} by default does not use XA datasources. However, this is considered unsafe if more than one datasource is used.
|
||||||
|
Starting with this release, you need to use XA datasources if you are adding additional datasources to {project_name}.
|
||||||
|
If the default datasource supports XA, you can do this by setting the `--transaction-xa-enabled=true` option. For additional datasources, you need to use
|
||||||
|
the `quarkus.datasource.<your-datasource-name>.jdbc.transactions=xa` option in your `quarkus.properties` file.
|
||||||
|
At most one datasource can be non-XA.
|
||||||
|
Recovery isn't supported when you don't have persistent storage for the transaction store.
|
||||||
|
|
||||||
= Hostname v1 feature removed
|
= Hostname v1 feature removed
|
||||||
|
|
||||||
The deprecated hostname v1 feature was removed. This feature was deprecated in {project_name} 25 and replaced by hostname v2. If you are still using this feature, you must migrate to hostname v2. For more details, see the https://www.keycloak.org/server/hostname[Configuring the hostname (v2)] and https://www.keycloak.org/docs/latest/upgrading/#new-hostname-options[the initial migration guide].
|
The deprecated hostname v1 feature was removed. This feature was deprecated in {project_name} 25 and replaced by hostname v2. If you are still using this feature, you must migrate to hostname v2. For more details, see the https://www.keycloak.org/server/hostname[Configuring the hostname (v2)] and https://www.keycloak.org/docs/latest/upgrading/#new-hostname-options[the initial migration guide].
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.quarkus.deployment;
|
||||||
|
|
||||||
|
import io.quarkus.builder.item.EmptyBuildItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
*/
|
||||||
|
public class CheckMultipleDatasourcesBuildStep extends EmptyBuildItem {}
|
|
@ -17,13 +17,17 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.deployment;
|
package org.keycloak.quarkus.deployment;
|
||||||
|
|
||||||
|
import io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig;
|
||||||
|
import io.quarkus.agroal.runtime.TransactionIntegration;
|
||||||
import io.quarkus.agroal.runtime.health.DataSourceHealthCheck;
|
import io.quarkus.agroal.runtime.health.DataSourceHealthCheck;
|
||||||
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
|
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
|
||||||
import io.quarkus.agroal.spi.JdbcDriverBuildItem;
|
import io.quarkus.agroal.spi.JdbcDriverBuildItem;
|
||||||
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
|
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
|
||||||
import io.quarkus.arc.deployment.BuildTimeConditionBuildItem;
|
import io.quarkus.arc.deployment.BuildTimeConditionBuildItem;
|
||||||
import io.quarkus.bootstrap.logging.InitialConfigurator;
|
import io.quarkus.bootstrap.logging.InitialConfigurator;
|
||||||
|
import io.quarkus.builder.item.EmptyBuildItem;
|
||||||
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem;
|
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem;
|
||||||
|
import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig;
|
||||||
import io.quarkus.deployment.IsDevelopment;
|
import io.quarkus.deployment.IsDevelopment;
|
||||||
import io.quarkus.deployment.annotations.BuildProducer;
|
import io.quarkus.deployment.annotations.BuildProducer;
|
||||||
import io.quarkus.deployment.annotations.BuildStep;
|
import io.quarkus.deployment.annotations.BuildStep;
|
||||||
|
@ -267,11 +271,31 @@ class KeycloakProcessor {
|
||||||
// We do not want to initialize the JDBC driver class
|
// We do not want to initialize the JDBC driver class
|
||||||
Class.forName(dbDriver.get(), false, Thread.currentThread().getContextClassLoader());
|
Class.forName(dbDriver.get(), false, Thread.currentThread().getContextClassLoader());
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
|
throwConfigError(String.format("Unable to find the JDBC driver (%s). You need to install it.", dbDriver.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspired by AgroalProcessor
|
||||||
|
@BuildStep
|
||||||
|
@Produce(CheckMultipleDatasourcesBuildStep.class)
|
||||||
|
void checkMultipleDatasourcesUseXA(DataSourcesBuildTimeConfig dataSourcesConfig, DataSourcesJdbcBuildTimeConfig jdbcConfig) {
|
||||||
|
long nonXADatasourcesCount = dataSourcesConfig.dataSources().keySet().stream()
|
||||||
|
.map(ds -> jdbcConfig.dataSources().get(ds).jdbc())
|
||||||
|
.filter(jdbc -> jdbc.enabled() && jdbc.transactions() != TransactionIntegration.XA)
|
||||||
|
.count();
|
||||||
|
if (nonXADatasourcesCount > 1) {
|
||||||
|
throwConfigError("Multiple datasources are configured but more than 1 is using non-XA transactions. " +
|
||||||
|
"All the datasources except one must must be XA to be able to use Last Resource Commit Optimization (LRCO). " +
|
||||||
|
"Please update your configuration by setting --transaction-xa-enabled=true " +
|
||||||
|
"and/or quarkus.datasource.<your-datasource-name>.jdbc.transactions=xa.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwConfigError(String msg) {
|
||||||
// Ignore queued TRACE and DEBUG messages for not initialized log handlers
|
// Ignore queued TRACE and DEBUG messages for not initialized log handlers
|
||||||
InitialConfigurator.DELAYED_HANDLER.setBuildTimeHandlers(new Handler[]{});
|
InitialConfigurator.DELAYED_HANDLER.setBuildTimeHandlers(new Handler[]{});
|
||||||
throw new ConfigurationException(String.format("Unable to find the JDBC driver (%s). You need to install it.", dbDriver.get()));
|
throw new ConfigurationException(msg);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,6 +368,7 @@ class KeycloakProcessor {
|
||||||
|
|
||||||
@BuildStep
|
@BuildStep
|
||||||
@Consume(CheckJdbcBuildStep.class)
|
@Consume(CheckJdbcBuildStep.class)
|
||||||
|
@Consume(CheckMultipleDatasourcesBuildStep.class)
|
||||||
void produceDefaultPersistenceUnit(BuildProducer<PersistenceXmlDescriptorBuildItem> producer) {
|
void produceDefaultPersistenceUnit(BuildProducer<PersistenceXmlDescriptorBuildItem> producer) {
|
||||||
ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit(
|
ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit(
|
||||||
Thread.currentThread().getContextClassLoader().getResource("default-persistence.xml"));
|
Thread.currentThread().getContextClassLoader().getResource("default-persistence.xml"));
|
||||||
|
|
|
@ -18,3 +18,4 @@
|
||||||
quarkus.datasource.user-store.db-kind=h2
|
quarkus.datasource.user-store.db-kind=h2
|
||||||
quarkus.datasource.user-store.username=sa
|
quarkus.datasource.user-store.username=sa
|
||||||
quarkus.datasource.user-store.jdbc.url=jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1
|
quarkus.datasource.user-store.jdbc.url=jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1
|
||||||
|
quarkus.datasource.user-store.jdbc.transactions=xa
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.it.cli.dist;
|
package org.keycloak.it.cli.dist;
|
||||||
|
|
||||||
import static io.restassured.RestAssured.when;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@ -114,6 +113,15 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
cliResult.assertStarted();
|
cliResult.assertStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(AddNonXADatasource.class)
|
||||||
|
@Launch({ "start" })
|
||||||
|
@Order(9)
|
||||||
|
void nonXADatasourceFailsToStart(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertError("Multiple datasources are configured but more than 1 is using non-XA transactions.");
|
||||||
|
}
|
||||||
|
|
||||||
public static class UpdateConsoleLogLevelToWarn implements Consumer<KeycloakDistribution> {
|
public static class UpdateConsoleLogLevelToWarn implements Consumer<KeycloakDistribution> {
|
||||||
@Override
|
@Override
|
||||||
public void accept(KeycloakDistribution distribution) {
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
@ -135,6 +143,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
distribution.setQuarkusProperty("quarkus.datasource.user-store.db-kind", "h2");
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.db-kind", "h2");
|
||||||
distribution.setQuarkusProperty("quarkus.datasource.user-store.username","sa");
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.username","sa");
|
||||||
distribution.setQuarkusProperty("quarkus.datasource.user-store.jdbc.url","jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1");
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.jdbc.url","jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.jdbc.transactions", "xa");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +154,17 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
distribution.setQuarkusProperty("quarkus.datasource.user-store2.db-transactions", "enabled");
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.db-transactions", "enabled");
|
||||||
distribution.setQuarkusProperty("quarkus.datasource.user-store2.username","sa");
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.username","sa");
|
||||||
distribution.setQuarkusProperty("quarkus.datasource.user-store2.jdbc.url","jdbc:h2:mem:user-store2;DB_CLOSE_DELAY=-1");
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.jdbc.url","jdbc:h2:mem:user-store2;DB_CLOSE_DELAY=-1");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.jdbc.transactions", "xa");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AddNonXADatasource implements Consumer<KeycloakDistribution> {
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store3.db-kind", "h2");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store3.db-transactions", "enabled");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store3.username","sa");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store3.jdbc.url","jdbc:h2:mem:user-store2;DB_CLOSE_DELAY=-1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue