diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index 0f26a00a77..5d8624336a 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -16,6 +16,8 @@ */ package org.apache.activemq.artemis.api.config; +import java.util.concurrent.TimeUnit; + import org.apache.activemq.artemis.ArtemisConstants; import org.apache.activemq.artemis.api.core.SimpleString; @@ -429,6 +431,8 @@ public final class ActiveMQDefaultConfiguration { // Default large messages table name, used with Database storage type private static final String DEFAULT_PAGE_STORE_TABLE_NAME = "PAGE_STORE"; + private static final int DEFAULT_JDBC_NETWORK_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(20); + // Default period to wait between connection TTL checks public static final long DEFAULT_CONNECTION_TTL_CHECK_INTERVAL = 2000; @@ -1172,6 +1176,10 @@ public final class ActiveMQDefaultConfiguration { return DEFAULT_PAGE_STORE_TABLE_NAME; } + public static int getDefaultJdbcNetworkTimeout() { + return DEFAULT_JDBC_NETWORK_TIMEOUT; + } + public static long getDefaultConnectionTtlCheckInterval() { return DEFAULT_CONNECTION_TTL_CHECK_INTERVAL; } diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java index 18289117c8..fcbf90d0ad 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java @@ -26,6 +26,7 @@ import java.sql.SQLWarning; import java.sql.Statement; import java.util.Arrays; import java.util.Properties; +import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -51,18 +52,28 @@ public abstract class AbstractJDBCDriver { private DataSource dataSource; + private Executor networkTimeoutExecutor; + + private int networkTimeoutMillis; + public AbstractJDBCDriver() { + this.networkTimeoutExecutor = null; + this.networkTimeoutMillis = -1; } public AbstractJDBCDriver(SQLProvider sqlProvider, String jdbcConnectionUrl, String jdbcDriverClass) { this.jdbcConnectionUrl = jdbcConnectionUrl; this.jdbcDriverClass = jdbcDriverClass; this.sqlProvider = sqlProvider; + this.networkTimeoutExecutor = null; + this.networkTimeoutMillis = -1; } public AbstractJDBCDriver(DataSource dataSource, SQLProvider provider) { this.dataSource = dataSource; this.sqlProvider = provider; + this.networkTimeoutExecutor = null; + this.networkTimeoutMillis = -1; } public void start() throws SQLException { @@ -76,6 +87,8 @@ public abstract class AbstractJDBCDriver { public AbstractJDBCDriver(Connection connection, SQLProvider sqlProvider) { this.connection = connection; this.sqlProvider = sqlProvider; + this.networkTimeoutExecutor = null; + this.networkTimeoutMillis = -1; } public void stop() throws SQLException { @@ -127,6 +140,17 @@ public abstract class AbstractJDBCDriver { throw e; } } + if (this.networkTimeoutMillis >= 0 && this.networkTimeoutExecutor != null) { + try { + connection.setNetworkTimeout(this.networkTimeoutExecutor, this.networkTimeoutMillis); + } catch (SQLException e) { + logger.warn(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e)); + ActiveMQJournalLogger.LOGGER.warn("Unable to set a network timeout on the JDBC connection"); + } catch (Throwable throwable) { + //it included SecurityExceptions and UnsupportedOperationException + logger.warn("Unable to set a network timeout on the JDBC connection", throwable); + } + } } } @@ -240,4 +264,9 @@ public abstract class AbstractJDBCDriver { this.dataSource = dataSource; } + public void setNetworkTimeout(Executor executor, int milliseconds) { + this.networkTimeoutExecutor = executor; + this.networkTimeoutMillis = milliseconds; + } + } diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java index d5a92a2329..ae2e793666 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java @@ -101,6 +101,14 @@ public class JDBCSequentialFileFactory implements SequentialFileFactory, ActiveM return dbDriver; } + /** + * @see Connection#setNetworkTimeout(Executor, int) + **/ + public JDBCSequentialFileFactory setNetworkTimeout(Executor executor, int milliseconds) { + this.dbDriver.setNetworkTimeout(executor, milliseconds); + return this; + } + @Override public SequentialFileFactory setDatasync(boolean enabled) { return this; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/storage/DatabaseStorageConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/storage/DatabaseStorageConfiguration.java index eb8b435ad4..76626c015c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/storage/DatabaseStorageConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/storage/DatabaseStorageConfiguration.java @@ -40,6 +40,8 @@ public class DatabaseStorageConfiguration implements StoreConfiguration { private SQLProvider.Factory sqlProviderFactory; + private int jdbcNetworkTimeout = ActiveMQDefaultConfiguration.getDefaultJdbcNetworkTimeout(); + @Override public StoreType getStoreType() { return StoreType.DATABASE; @@ -125,4 +127,12 @@ public class DatabaseStorageConfiguration implements StoreConfiguration { public void setSqlProvider(SQLProvider.Factory sqlProviderFactory) { this.sqlProviderFactory = sqlProviderFactory; } + + public int getJdbcNetworkTimeout() { + return this.jdbcNetworkTimeout; + } + + public void setJdbcNetworkTimeout(int jdbcNetworkTimeout) { + this.jdbcNetworkTimeout = jdbcNetworkTimeout; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 087a6fb0b3..c36699e583 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -1159,6 +1159,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { conf.setPageStoreTableName(getString(storeNode, "page-store-table-name", conf.getPageStoreTableName(), Validators.NO_CHECK)); conf.setJdbcConnectionUrl(getString(storeNode, "jdbc-connection-url", conf.getJdbcConnectionUrl(), Validators.NO_CHECK)); conf.setJdbcDriverClassName(getString(storeNode, "jdbc-driver-class-name", conf.getJdbcDriverClassName(), Validators.NO_CHECK)); + conf.setJdbcNetworkTimeout(getInteger(storeNode, "jdbc-network-timeout", conf.getJdbcNetworkTimeout(), Validators.NO_CHECK)); return conf; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryDatabase.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryDatabase.java index afe97089e9..a8c6127cae 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryDatabase.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryDatabase.java @@ -117,6 +117,10 @@ public class PagingStoreFactoryDatabase implements PagingStoreFactory { String driverClassName = dbConf.getJdbcDriverClassName(); pagingFactoryFileFactory = new JDBCSequentialFileFactory(dbConf.getJdbcConnectionUrl(), driverClassName, JDBCUtils.getSQLProvider(driverClassName, pageStoreTableNamePrefix, SQLProvider.DatabaseStoreType.PAGE), executorFactory.getExecutor(), criticalErrorListener); } + final int jdbcNetworkTimeout = dbConf.getJdbcNetworkTimeout(); + if (jdbcNetworkTimeout >= 0) { + pagingFactoryFileFactory.setNetworkTimeout(this.executorFactory.getExecutor(), jdbcNetworkTimeout); + } pagingFactoryFileFactory.start(); started = true; } @@ -224,8 +228,12 @@ public class PagingStoreFactoryDatabase implements PagingStoreFactory { } else { sqlProvider = JDBCUtils.getSQLProvider(dbConf.getJdbcDriverClassName(), getTableNameForGUID(directoryName), SQLProvider.DatabaseStoreType.PAGE); } - - return new JDBCSequentialFileFactory(pagingFactoryFileFactory.getDbDriver().getConnection(), sqlProvider, executorFactory.getExecutor(), criticalErrorListener); + final JDBCSequentialFileFactory fileFactory = new JDBCSequentialFileFactory(pagingFactoryFileFactory.getDbDriver().getConnection(), sqlProvider, executorFactory.getExecutor(), criticalErrorListener); + final int jdbcNetworkTimeout = dbConf.getJdbcNetworkTimeout(); + if (jdbcNetworkTimeout >= 0) { + fileFactory.setNetworkTimeout(this.executorFactory.getExecutor(), jdbcNetworkTimeout); + } + return fileFactory; } private String getTableNameForGUID(String guid) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java index 5592c9e2a5..4b8392da76 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java @@ -55,8 +55,10 @@ public class JDBCJournalStorageManager extends JournalStorageManager { @Override protected synchronized void init(Configuration config, IOCriticalErrorListener criticalErrorListener) { try { - DatabaseStorageConfiguration dbConf = (DatabaseStorageConfiguration) config.getStoreConfiguration(); - + final DatabaseStorageConfiguration dbConf = (DatabaseStorageConfiguration) config.getStoreConfiguration(); + final JDBCJournalImpl bindingsJournal; + final JDBCJournalImpl messageJournal; + final JDBCSequentialFileFactory largeMessagesFactory; if (dbConf.getDataSource() != null) { SQLProvider.Factory sqlProviderFactory = dbConf.getSqlProviderFactory(); if (sqlProviderFactory == null) { @@ -71,6 +73,19 @@ public class JDBCJournalStorageManager extends JournalStorageManager { messageJournal = new JDBCJournalImpl(dbConf.getJdbcConnectionUrl(), driverClassName, JDBCUtils.getSQLProvider(driverClassName, dbConf.getMessageTableName(), SQLProvider.DatabaseStoreType.MESSAGE_JOURNAL), scheduledExecutorService, executorFactory.getExecutor(), criticalErrorListener); largeMessagesFactory = new JDBCSequentialFileFactory(dbConf.getJdbcConnectionUrl(), driverClassName, JDBCUtils.getSQLProvider(driverClassName, dbConf.getLargeMessageTableName(), SQLProvider.DatabaseStoreType.LARGE_MESSAGE), executor, criticalErrorListener); } + final int networkTimeout = dbConf.getJdbcNetworkTimeout(); + if (networkTimeout >= 0) { + bindingsJournal.setNetworkTimeout(executorFactory.getExecutor(), networkTimeout); + } + if (networkTimeout >= 0) { + messageJournal.setNetworkTimeout(executorFactory.getExecutor(), networkTimeout); + } + if (networkTimeout >= 0) { + largeMessagesFactory.setNetworkTimeout(executorFactory.getExecutor(), networkTimeout); + } + this.bindingsJournal = bindingsJournal; + this.messageJournal = messageJournal; + this.largeMessagesFactory = largeMessagesFactory; largeMessagesFactory.start(); } catch (Exception e) { criticalErrorListener.onIOException(e, e.getMessage(), null); diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index af0148e30e..6fa5fc45cd 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -1707,6 +1707,13 @@ + + + + The JDBC network connection timeout in milliseconds. + + + diff --git a/docs/user-manual/en/persistence.md b/docs/user-manual/en/persistence.md index 50d9dc9462..7e07773eed 100644 --- a/docs/user-manual/en/persistence.md +++ b/docs/user-manual/en/persistence.md @@ -298,9 +298,9 @@ The message journal is configured using the following attributes in data files on the journal The default for this parameter is `30` - + - `journal-datasync` (default: true) - + This will disable the use of fdatasync on journal writes. ### An important note on disabling disk write cache. @@ -424,6 +424,10 @@ To configure Apache ActiveMQ Artemis to use a database for persisting messages a The fully qualified class name of the desired database Driver. +- `jdbc-network-timeout` + + The JDBC network connection timeout in milliseconds. The default value + is 20000 milliseconds (ie 20 seconds). ## Configuring Apache ActiveMQ Artemis for Zero Persistence