Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/configurations/alpine-temurin21-mysql.compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ services:
condition: service_healthy
environment:
DATABASE: mysql
DATABASE_URL: jdbc:mysql://db:3306/mirthdb
DATABASE_URL: jdbc:mysql://db:3306/mirthdb?sessionVariables=transaction_isolation='READ-COMMITTED'
DATABASE_USERNAME: mirthdb
DATABASE_PASSWORD: mirthdb
DATABASE_MAX_CONNECTIONS: 20
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ public JdbcDao getDao(SerializerProvider serializerProvider) {
return getDao(donkey, connection, querySource, statementSource, serializerProvider, encryptMessageContent, encryptAttachments, encryptCustomMetaData, decryptData, statisticsUpdater, channelController.getStatistics(), channelController.getTotalStatistics(), statsServerId);
}

/** Assert a brand new database and server is acceptable */
public void checkNewDatabase(Connection connection) {
// By default, all new databases are considered acceptable.
}

protected JdbcDao getDao(Donkey donkey, Connection connection, QuerySource querySource, PreparedStatementSource statementSource, SerializerProvider serializerProvider, boolean encryptMessageContent, boolean encryptAttachments, boolean encryptCustomMetaData, boolean decryptData, StatisticsUpdater statisticsUpdater, Statistics currentStats, Statistics totalStats, String statsServerId) {
return new JdbcDao(donkey, connection, querySource, statementSource, serializerProvider, encryptMessageContent, encryptAttachments, encryptCustomMetaData, decryptData, statisticsUpdater, currentStats, totalStats, statsServerId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,55 @@

package com.mirth.connect.donkey.server.data.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.mirth.connect.donkey.server.data.DonkeyDaoException;
import com.mirth.connect.donkey.util.SerializerProvider;

public class MysqlDaoFactory extends JdbcDaoFactory {
private static final Logger logger = LogManager.getLogger(MysqlDaoFactory.class);
private final AtomicBoolean hasCheckedTransactionIsolation = new AtomicBoolean(false);

@Override
public JdbcDao getDao(SerializerProvider serializerProvider) {
JdbcDao dao = super.getDao(serializerProvider);
dao.setQuoteChar('`');
checkTransactionIsolation(dao);
return dao;
}

@Override
public void checkNewDatabase(Connection connection) {
try {
if (connection.getTransactionIsolation() == Connection.TRANSACTION_REPEATABLE_READ) {
throw new DonkeyDaoException("Creating a new database with REPEATABLE-READ transaction "
+ "isolation is not allowed. Set the transaction isolation level in the database "
+ "URL or the database server to READ-COMMITTED.");
}
} catch (SQLException e) {
throw new DonkeyDaoException("Failed to check the transaction isolation level for this MySQL connection.", e);
}
}

/**
* Warns once if the connection is using the default REPEATABLE-READ transaction isolation level.
*/
private void checkTransactionIsolation(JdbcDao dao) {
if (!hasCheckedTransactionIsolation.compareAndSet(false, true)) {
return;
}

try {
if (dao.getConnection().getTransactionIsolation() == Connection.TRANSACTION_REPEATABLE_READ) {
logger.error("This MySQL connection is using the REPEATABLE-READ transaction isolation level. The MySQL REPEATABLE-READ will cause intermittent issues. Set the transaction isolation level in the database URL or the database server to READ-COMMITTED to avoid these problems.");
}
} catch (SQLException e) {
logger.error("Failed to check the transaction isolation level for this MySQL connection.", e);
}
}
}
2 changes: 1 addition & 1 deletion server/conf/mirth.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ database = derby
# examples:
# Derby jdbc:derby:${dir.appdata}/mirthdb;create=true
# PostgreSQL jdbc:postgresql://localhost:5432/mirthdb
# MySQL jdbc:mysql://localhost:3306/mirthdb
# MySQL jdbc:mysql://localhost:3306/mirthdb?sessionVariables=transaction_isolation='READ-COMMITTED'
# Oracle jdbc:oracle:thin:@localhost:1521:DB
# SQL Server/Sybase (jTDS) jdbc:jtds:sqlserver://localhost:1433/mirthdb
# Microsoft SQL Server jdbc:sqlserver://localhost:1433;databaseName=mirthdb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

import com.mirth.connect.client.core.PropertiesConfigurationUtil;
import com.mirth.connect.client.core.Version;
import com.mirth.connect.donkey.server.data.DonkeyDaoException;
import com.mirth.connect.donkey.server.data.jdbc.JdbcDaoFactory;
import com.mirth.connect.model.Channel;
import com.mirth.connect.model.ExportClearable;
import com.mirth.connect.model.alert.AlertModel;
Expand Down Expand Up @@ -258,6 +260,14 @@ private Migrator getMigrator(Version version) {
private void initDatabase(Connection connection) throws MigrationException {
// If missing this table we can assume that they don't have the schema installed
if (!DatabaseUtil.tableExists(connection, "CONFIGURATION")) {
// Since we are creating a new database, let the database-specific DAO
// factory check if it is acceptable.
try {
JdbcDaoFactory.getInstance(getDatabaseType()).checkNewDatabase(connection);
} catch (DonkeyDaoException e) {
throw new MigrationException(e.getMessage(), e);
}

executeScript("/" + getDatabaseType() + "/" + getDatabaseType() + "-database.sql");

initializeAdminPassword();
Expand Down
Loading