Фон
У нас есть 2 сервиса, написанных на Java: один обрабатывает операции с базами данных над разными файлами (CRUD над базой данных), а другой - надолго обрабатывает эти записи (сложные фоновые задачи). Просто мы могли бы сказать, что они являются производителем и потребителем.
Предполагаемое поведение выглядит следующим образом:
Сервис 1 (используется код ниже):
Сохранить файл в БД
Если файл типа 'C', поместите его в очередь сообщений для дальнейшей обработки
Сервис 2:
Получение сообщения из очереди сообщений
Загрузить файл из базы данных (по идентификатору)
Выполнить дальнейшую обработку
Код службы 1 выглядит следующим образом (я изменил некоторые названия по корпоративным причинам)
private void persist() throws Exception {
try (SqlSession sqlSession = sessionFactory.openSession()) {
FileType fileType = FileType.fromFileName(filename);
FileEntity dto = new FileEntity(filename, currentTime(), null, user.getName(), count, data);
oracleFileStore.create(sqlSession, dto);
auditLog.logFileUploaded(user, filename, count);
sqlSession.commit();
if (fileType == FileType.C) {
mqClient.submit(new Record(dto.getId(), dto.getName(), user));
auditLog.logCFileDetected(user, filename);
}
}
}
Дополнительная информация
ActiveMQ 5.15 используется для очереди сообщений
База данных Oracle 12c
База данных обрабатывается Mybatis 3.4.1
Проблема
Время от времени случается, что Служба 2 получает сообщение от MQ, пытается прочитать файл из базы данных и, что удивительно, файла там нет. Инцидент довольно редкий, но это случается. Когда мы проверяем базу данных, файл там. Похоже, что фоновая обработка файла началась до того, как файл был помещен в базу данных.
Вопросы
Возможно ли, что MQ-вызов может быть быстрее, чем фиксация базы данных? Я создал файл в БД, он называется commit и только после этого я помещаю сообщение в MQ. MQ даже содержит идентификатор, который генерируется самой базой данных (последовательность).
Нужно ли закрывать соединение, чтобы убедиться, что фиксация была выполнена? Я всегда думал, что когда я фиксирую, он находится в базе данных независимо от того, закончилась ли моя транзакция.
Может ли проблема быть Mybatis? Я прочитал некоторые проблемы, касающиеся транзакций / сеансов Mybatis, но это не похоже на мою проблему
Update
Я могу предоставить дополнительный код, хотя, пожалуйста, поймите, что я не могу поделиться всем по корпоративным причинам. Если вы не видите ничего очевидного в этом, это нормально. К сожалению, я не могу продолжать гораздо более глубокий анализ, чем этот.
Кроме того, я хотел подтвердить, правильно ли я понимаю SQL и Mybatis, и я также могу пометить такой ответ как правильный.
SessionFactory.java (отрывок)
private SqlSessionFactory createLegacySessionFactory(DataSource dataSource) throws Exception
{
Configuration configuration = prepareConfiguration(dataSource);
return new SqlSessionFactoryBuilder().build(configuration);
}
//javax.sql.DataSource
private Configuration prepareConfiguration(DataSource dataSource)
{
//classes from package org.apache.ibatis
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
addSettings(configuration);
addTypeAliases(configuration);
addTypeHandlers(configuration);
configuration.addMapper(PermissionMapper.class);
addMapperXMLs(configuration); //just add all the XML mappers
return configuration;
}
public SqlSession openSession()
{
//Initialization of factory is above
return new ForceCommitSqlSession(factory.openSession());
}
ForceCommitSqlSession.java (отрывок)
/**
* ForceCommitSqlSession is wrapper around mybatis {@link SqlSession}.
* <p>
* Its purpose is to force commit/rollback during standard commit/rollback operations. The default implementation (according to javadoc)
* does
* not commit/rollback if there were no changes to the database - this can lead to problems, when operations are executed outside mybatis
* session (e.g. via {@link #getConnection()}).
*/
public class ForceCommitSqlSession implements SqlSession
{
private final SqlSession session;
/**
* Force the commit all the time (despite "generic contract")
*/
@Override
public void commit()
{
session.commit(true);
}
/**
* Force the roll back all the time (despite "generic contract")
*/
@Override
public void rollback()
{
session.rollback(true);
}
@Override
public int insert(String statement)
{
return session.insert(statement);
}
....
}
OracleFileStore.java (отрывок)
public int create(SqlSession session, FileEntity fileEntity) throws Exception
{
//the mybatis xml is simple insert SQL query
return session.insert(STATEMENT_CREATE, fileEntity);
}