JTA transaction support for Quarkus applications with programmatic and declarative transaction management
—
Comprehensive configuration system supporting node identification, timeouts, object store types, recovery options, and runtime customization for Quarkus transaction management.
Core configuration properties for transaction manager runtime behavior.
# Node identification
quarkus.transaction-manager.node-name=quarkus
quarkus.transaction-manager.shorten-node-name-if-necessary=false
# Transaction timeouts
quarkus.transaction-manager.default-transaction-timeout=60s
# Recovery system
quarkus.transaction-manager.enable-recovery=false
quarkus.transaction-manager.recovery-modules=com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule,com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter
quarkus.transaction-manager.expiry-scanners=com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner
quarkus.transaction-manager.xa-resource-orphan-filters=com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter,com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter
# Object Store - File System (default)
quarkus.transaction-manager.object-store.type=file-system
quarkus.transaction-manager.object-store.directory=ObjectStore
# Object Store - JDBC
quarkus.transaction-manager.object-store.type=jdbc
quarkus.transaction-manager.object-store.datasource=<datasource-name>
quarkus.transaction-manager.object-store.create-table=false
quarkus.transaction-manager.object-store.drop-table=false
quarkus.transaction-manager.object-store.table-prefix=quarkus_Runtime configuration interface for programmatic access to transaction manager settings.
/**
* Runtime configuration interface for transaction manager
*/
interface TransactionManagerConfiguration {
/**
* Node identifier for transaction manager
* Used for transaction recovery and distributed transaction coordination
* @return Node name string (default: "quarkus")
*/
String nodeName();
/**
* Whether to automatically shorten node name if it exceeds system limits
* @return true if automatic shortening is enabled (default: false)
*/
boolean shortenNodeNameIfNecessary();
/**
* Default timeout for transactions when not explicitly specified
* @return Duration representing default timeout (default: 60 seconds)
*/
Duration defaultTransactionTimeout();
/**
* Whether transaction recovery is enabled
* Recovery allows incomplete transactions to be resolved after crashes
* @return true if recovery is enabled (default: false)
*/
boolean enableRecovery();
/**
* List of recovery module class names for transaction recovery
* @return List of fully qualified class names
*/
List<String> recoveryModules();
/**
* List of expiry scanner class names for cleaning up expired transactions
* @return List of fully qualified class names
*/
List<String> expiryScanners();
/**
* List of XA resource orphan filter class names
* @return List of fully qualified class names
*/
List<String> xaResourceOrphanFilters();
/**
* Object store configuration for transaction logs
* @return ObjectStoreConfig instance
*/
ObjectStoreConfig objectStore();
}Usage Examples:
import io.quarkus.narayana.jta.runtime.TransactionManagerConfiguration;
@ApplicationScoped
public class TransactionMonitoringService {
@Inject
TransactionManagerConfiguration config;
public void logConfiguration() {
logger.info("Node name: " + config.nodeName());
logger.info("Default timeout: " + config.defaultTransactionTimeout());
logger.info("Recovery enabled: " + config.enableRecovery());
}
public boolean isRecoveryEnabled() {
return config.enableRecovery();
}
public Duration getDefaultTimeout() {
return config.defaultTransactionTimeout();
}
}Configuration for transaction log storage, supporting both file system and JDBC storage.
/**
* Object store configuration for transaction persistence
*/
interface ObjectStoreConfig {
/**
* Directory for file system object store
* @return Directory path (default: "ObjectStore")
*/
String directory();
/**
* Type of object store to use
* @return ObjectStoreType enum value
*/
ObjectStoreType type();
/**
* Datasource name for JDBC object store
* Only used when type is JDBC
* @return Optional datasource name
*/
Optional<String> datasource();
/**
* Whether to create object store table automatically
* Only applies to JDBC object store
* @return true if table should be created (default: false)
*/
boolean createTable();
/**
* Whether to drop object store table on shutdown
* Only applies to JDBC object store
* @return true if table should be dropped (default: false)
*/
boolean dropTable();
/**
* Prefix for object store table names
* Only applies to JDBC object store
* @return Table prefix (default: "quarkus_")
*/
String tablePrefix();
}
/**
* Object store type enumeration
*/
enum ObjectStoreType {
/** File system based storage (default) */
File_System,
/** JDBC database based storage */
JDBC
}Configuration Examples:
# File System Object Store (Development)
quarkus.transaction-manager.object-store.type=file-system
quarkus.transaction-manager.object-store.directory=/app/transaction-logs
# JDBC Object Store (Production)
quarkus.transaction-manager.object-store.type=jdbc
quarkus.transaction-manager.object-store.datasource=transaction-log
quarkus.transaction-manager.object-store.create-table=true
quarkus.transaction-manager.object-store.table-prefix=app_tx_/**
* Node name configuration affects transaction recovery and clustering
*/
@ApplicationScoped
public class NodeConfigurationService {
@ConfigProperty(name = "quarkus.transaction-manager.node-name")
String nodeName;
@ConfigProperty(name = "quarkus.transaction-manager.shorten-node-name-if-necessary")
boolean shortenNodeName;
public String getEffectiveNodeName() {
String effectiveName = nodeName;
if (shortenNodeName && effectiveName.length() > 28) {
// Narayana has a 28-character limit for node names
effectiveName = effectiveName.substring(0, 28);
logger.info("Node name shortened from {} to {}", nodeName, effectiveName);
}
return effectiveName;
}
}Multiple ways to configure transaction timeouts at different levels.
/**
* Timeout configuration hierarchy (highest precedence first):
* 1. Programmatic timeout (QuarkusTransaction.beginOptions().timeout())
* 2. @TransactionConfiguration annotation timeout
* 3. Configuration property referenced by @TransactionConfiguration
* 4. Global default timeout configuration
*/Configuration Examples:
# Global default timeout
quarkus.transaction-manager.default-transaction-timeout=120s
# Custom timeout properties for specific use cases
batch.processing.timeout=600
critical.operation.timeout=30
quick.operation.timeout=5Usage in Code:
@ApplicationScoped
public class TimeoutConfigurationService {
// Global default (120s from config)
@Transactional
public void standardOperation() { }
// Method-level timeout override
@Transactional
@TransactionConfiguration(timeout = 60)
public void mediumOperation() { }
// Property-based timeout with fallback
@Transactional
@TransactionConfiguration(
timeout = 30, // Fallback value
timeoutFromConfigProperty = "critical.operation.timeout"
)
public void criticalOperation() { }
// Programmatic timeout (highest precedence)
public void programmaticTimeout() {
QuarkusTransaction.begin(
QuarkusTransaction.beginOptions().timeout(45)
);
// This uses 45s regardless of other configurations
}
}Transaction recovery configuration for production environments.
# Enable recovery for production
quarkus.transaction-manager.enable-recovery=true
# Recovery with JDBC object store
quarkus.transaction-manager.object-store.type=jdbc
quarkus.transaction-manager.object-store.datasource=recovery-db
quarkus.transaction-manager.object-store.create-table=trueRecovery Service Usage:
@ApplicationScoped
public class RecoveryMonitoringService {
@Inject
TransactionManagerConfiguration config;
@Scheduled(every = "1h")
public void checkRecoveryStatus() {
if (config.enableRecovery()) {
logger.info("Transaction recovery is enabled");
// Monitor recovery process
checkForOrphanedTransactions();
}
}
private void checkForOrphanedTransactions() {
// Implementation depends on object store type
if (config.objectStore().type() == ObjectStoreType.JDBC) {
checkJdbcObjectStoreHealth();
} else {
checkFileSystemObjectStoreHealth();
}
}
}Development Configuration:
# Development - simple file-based storage
quarkus.transaction-manager.node-name=dev-node
quarkus.transaction-manager.default-transaction-timeout=30s
quarkus.transaction-manager.enable-recovery=false
quarkus.transaction-manager.object-store.type=file-system
quarkus.transaction-manager.object-store.directory=target/tx-logsProduction Configuration:
# Production - JDBC storage with recovery
quarkus.transaction-manager.node-name=${hostname:prod-node}
quarkus.transaction-manager.default-transaction-timeout=60s
quarkus.transaction-manager.enable-recovery=true
quarkus.transaction-manager.object-store.type=jdbc
quarkus.transaction-manager.object-store.datasource=transaction-log
quarkus.transaction-manager.object-store.create-table=false
quarkus.transaction-manager.object-store.table-prefix=prod_tx_Kubernetes/Cloud Configuration:
# Cloud-native configuration
quarkus.transaction-manager.node-name=${HOSTNAME:${RANDOM_UUID}}
quarkus.transaction-manager.shorten-node-name-if-necessary=true
quarkus.transaction-manager.default-transaction-timeout=120s
quarkus.transaction-manager.enable-recovery=true
quarkus.transaction-manager.object-store.type=jdbc
quarkus.transaction-manager.object-store.datasource=shared-transaction-logHibernate ORM Integration:
# Ensure transaction manager is available before Hibernate
quarkus.hibernate-orm.transaction-manager=quarkus-jta
# Database configuration for both app data and transaction logs
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=app_user
quarkus.datasource.password=app_password
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/app_db
# Separate datasource for transaction logs
quarkus.datasource.tx-log.db-kind=postgresql
quarkus.datasource.tx-log.username=tx_user
quarkus.datasource.tx-log.password=tx_password
quarkus.datasource.tx-log.jdbc.url=jdbc:postgresql://localhost/tx_log_db
quarkus.transaction-manager.object-store.datasource=tx-logJMS Integration:
# JMS with XA transactions
quarkus.artemis.xa=true
quarkus.transaction-manager.enable-recovery=true# ✅ Good: Descriptive and unique node names
quarkus.transaction-manager.node-name=order-service-prod-01
quarkus.transaction-manager.node-name=payment-processor-${HOSTNAME}
# ✅ Good: Use environment variables for dynamic naming
quarkus.transaction-manager.node-name=${NODE_NAME:default-node}
# ❌ Avoid: Generic names in clustered environments
quarkus.transaction-manager.node-name=node1# ✅ Good: Different timeouts for different operation types
quarkus.transaction-manager.default-transaction-timeout=60s
batch.processing.timeout=1800s
quick.query.timeout=10s
external.api.timeout=30s
# ✅ Good: Use configuration properties for flexibility
@TransactionConfiguration(timeoutFromConfigProperty = "batch.processing.timeout")# ✅ Development: File system for simplicity
%dev.quarkus.transaction-manager.object-store.type=file-system
%dev.quarkus.transaction-manager.enable-recovery=false
# ✅ Production: JDBC for reliability and clustering
%prod.quarkus.transaction-manager.object-store.type=jdbc
%prod.quarkus.transaction-manager.enable-recovery=true
%prod.quarkus.transaction-manager.object-store.create-table=false@ApplicationScoped
public class TransactionMetricsService {
@Inject
TransactionManagerConfiguration config;
@Inject
MeterRegistry meterRegistry;
@PostConstruct
void initMetrics() {
Gauge.builder("transaction.default.timeout.seconds")
.register(meterRegistry, this,
service -> service.config.defaultTransactionTimeout().toSeconds());
Gauge.builder("transaction.recovery.enabled")
.register(meterRegistry, this,
service -> service.config.enableRecovery() ? 1 : 0);
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-narayana-jta