Skip to content

Commit

Permalink
Prohibit using Narayana JDBC object store with Agroal XA
Browse files Browse the repository at this point in the history
  • Loading branch information
michalvavrik authored and gsmet committed Jul 28, 2023
1 parent 642cd71 commit fd02a43
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.narayana.jta.deployment;

import static io.quarkus.datasource.common.runtime.DataSourceUtil.dataSourcePropertyKeys;
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;

import java.util.HashMap;
Expand Down Expand Up @@ -52,6 +53,7 @@
import io.quarkus.deployment.annotations.Produce;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.ConfigurationBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem;
Expand Down Expand Up @@ -153,12 +155,40 @@ public void build(NarayanaJtaRecorder recorder,
@Record(RUNTIME_INIT)
@Consume(NarayanaInitBuildItem.class)
@Consume(SyntheticBeansRuntimeInitBuildItem.class)
public void startRecoveryService(NarayanaJtaRecorder recorder,
public void startRecoveryService(NarayanaJtaRecorder recorder, ConfigurationBuildItem configurationBuildItem,
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems, TransactionManagerConfiguration transactions) {
Map<Boolean, String> namedDataSources = new HashMap<>();

jdbcDataSourceBuildItems.forEach(i -> namedDataSources.put(i.isDefault(), i.getName()));
recorder.startRecoveryService(transactions, namedDataSources);
Map<String, String> dsToConfigKey = getDsWithoutDisabledTransactions(
configurationBuildItem.getReadResult().getBuildTimeRunTimeValues(), namedDataSources);

recorder.startRecoveryService(transactions, namedDataSources, dsToConfigKey);
}

private static Map<String, String> getDsWithoutDisabledTransactions(Map<String, String> buildTimeRunTimeValues,
Map<Boolean, String> dataSources) {
Map<String, String> dsToTransactionIntegration = new HashMap<>();
for (String dataSourceName : dataSources.values()) {
List<String> transactionsPropertyKeys = dataSourcePropertyKeys(dataSourceName, "jdbc.transactions");
boolean propertyExplicitlyDefined = false;
for (String propertyKey : transactionsPropertyKeys) {
// get datasource 'io.quarkus.agroal.runtime.TransactionIntegration'
String transactionIntegration = buildTimeRunTimeValues.get(propertyKey);
if (transactionIntegration != null) {
propertyExplicitlyDefined = true;
if (!"disabled".equalsIgnoreCase(transactionIntegration)) {
dsToTransactionIntegration.put(dataSourceName, propertyKey);
break;
}
}
}
if (!propertyExplicitlyDefined) {
// by default transaction capabilities are enabled
dsToTransactionIntegration.put(dataSourceName, transactionsPropertyKeys.get(0));
}
}
return dsToTransactionIntegration;
}

@BuildStep(onlyIf = IsTest.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.quarkus.narayana.quarkus;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.List;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.builder.Version;
import io.quarkus.maven.dependency.Dependency;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.runtime.util.ExceptionUtil;
import io.quarkus.test.QuarkusUnitTest;

public class TransactionJdbcObjectStoreValidationFailureTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource("jdbc-object-store-validation.properties", "application.properties"))
.setForcedDependencies(List.of(Dependency.of("io.quarkus", "quarkus-jdbc-h2", Version.getVersion())))
.assertException(t -> {
Throwable rootCause = ExceptionUtil.getRootCause(t);
if (rootCause instanceof ConfigurationException) {
assertTrue(rootCause.getMessage().contains(
"The Narayana JTA extension is configured to use the datasource 'test' but that datasource is not configured."));
} else {
fail(t);
}
});

@Test
public void test() {
// needs to be there in order to run test
Assertions.fail("Application was supposed to fail.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
quarkus.transaction-manager.object-store.type=jdbc
quarkus.transaction-manager.object-store.datasource=test
quarkus.datasource.test.db-kind=h2
quarkus.datasource.test.jdbc.url=jdbc:h2:mem:default
quarkus.datasource.test.jdbc.transactions=xa

# we must not start database as CI is also executed on Windows without (Docker) Linux containers
quarkus.datasource.devservices.enabled=false
quarkus.devservices.enabled=false
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ private void setJDBCObjectStore(String name, TransactionManagerConfiguration con
instance.setTablePrefix(config.objectStore.tablePrefix);
}

public void startRecoveryService(final TransactionManagerConfiguration transactions, Map<Boolean, String> dataSources) {
public void startRecoveryService(final TransactionManagerConfiguration transactions, Map<Boolean, String> dataSources,
Map<String, String> dsWithInvalidTransactionsToConfigKey) {
if (transactions.objectStore.type.equals(ObjectStoreType.JDBC)) {
final String dsName;
if (transactions.objectStore.datasource.isEmpty()) {
dataSources.keySet().stream().filter(i -> i).findFirst().orElseThrow(
() -> new ConfigurationException(
dsName = dataSources.entrySet().stream().filter(Map.Entry::getKey).map(Map.Entry::getValue).findFirst()
.orElseThrow(() -> new ConfigurationException(
"The Narayana JTA extension does not have a datasource configured,"
+ " so it defaults to the default datasource,"
+ " but that datasource is not configured."
Expand All @@ -117,7 +119,7 @@ public void startRecoveryService(final TransactionManagerConfiguration transacti
+ " or configure the datasource to use in the Narayana JTA extension "
+ " by setting property 'quarkus.transaction-manager.object-store.datasource' to the name of a configured datasource."));
} else {
String dsName = transactions.objectStore.datasource.get();
dsName = transactions.objectStore.datasource.get();
dataSources.values().stream().filter(i -> i.equals(dsName)).findFirst()
.orElseThrow(() -> new ConfigurationException(
"The Narayana JTA extension is configured to use the datasource '"
Expand All @@ -128,6 +130,16 @@ public void startRecoveryService(final TransactionManagerConfiguration transacti
+ " or configure another datasource to use in the Narayana JTA extension "
+ " by setting property 'quarkus.transaction-manager.object-store.datasource' to the name of a configured datasource."));
}
if (dsWithInvalidTransactionsToConfigKey.containsKey(dsName)) {
throw new ConfigurationException(String.format(
"The Narayana JTA extension is configured to use the '%s' JDBC "
+ "datasource as the transaction log storage, "
+ "but that datasource does not have transaction capabilities disabled. "
+ "To solve this, please set '%s=disabled', or configure another datasource "
+ "with disabled transaction capabilities as the JDBC object store. "
+ "Please refer to the https://quarkus.io/guides/transaction#jdbcstore for more information.",
dsName, dsWithInvalidTransactionsToConfigKey.get(dsName)));
}
}
if (transactions.enableRecovery) {
QuarkusRecoveryService.getInstance().create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ quarkus.log.category."io.quarkus".level=INFO
quarkus.datasource.db-kind=h2

quarkus.transaction-manager.object-store.directory=target/tx-object-store

quarkus.datasource.test.jdbc.transactions=disabled
quarkus.datasource.test.db-kind=h2
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ public Map<String, String> getConfigOverrides() {
HashMap<String, String> props = new HashMap<>();
props.put("quarkus.transaction-manager.object-store.type", "jdbc");
props.put("quarkus.transaction-manager.object-store.create-table", "true");
props.put("quarkus.transaction-manager.object-store.datasource", "test");
props.put("quarkus.transaction-manager.enable-recovery", "true");

props.put("quarkus.datasource.test.db-kind", "h2");
props.put("quarkus.datasource.test.jdbc.url", "jdbc:h2:mem:default");
props.put("quarkus.datasource.test.jdbc.transactions", "xa");

return props;
}
Expand Down

0 comments on commit fd02a43

Please sign in to comment.