Mybatis SQL-сеанс фиксируется на вид медленнее, чем следующий код
Фон
У нас есть 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, но это не похоже на мою проблему
Обновить
Я могу предоставить дополнительный код, хотя, пожалуйста, поймите, что я не могу поделиться всем по корпоративным причинам. Если вы не видите ничего очевидного в этом, это нормально. К сожалению, я не могу продолжать гораздо более глубокий анализ, чем этот.
Также я в основном хотел подтвердить, правильно ли я понимаю 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);
}
1 ответ
Возможно ли, что MQ-вызов может быть быстрее, чем фиксация базы данных?
Если фиксация базы данных сделана, изменения будут в базе данных. Создание задачи в очереди происходит после этого. Главное, что вам нужно проверить, что фиксация происходит синхронно, когда вы вызываете commit
на сессию. Из представленной вами конфигурации это выглядит нормально, если только нет Connection
сам. Я могу представить, что есть какая-то обертка над родной Connection
например. Я бы проверил в отладчике, что commit
вызов вызывает вызов Connection.commit
на реализацию из драйвера JDBC оракула. Еще лучше проверить логи на стороне БД.
Нужно ли закрывать соединение, чтобы убедиться, что фиксация была выполнена? Я всегда думал, что когда я фиксирую, то это находится в базе данных независимо от того, закончилась ли моя транзакция или нет.
Ты прав. Нет необходимости закрывать соединение, которое соответствует спецификации JDBC (это делает собственное соединение JDCB). Конечно, вы всегда можете создать какую-то обертку, которая не подчиняется Connection
API и делает некоторую магию (например, задерживает коммит, пока соединение не будет закрыто).
Может ли проблема быть Mybatis? Я прочитал некоторые проблемы, касающиеся транзакций / сеансов Mybatis, но это не похоже на мою проблему
Я бы сказал, что это маловероятно. Ты используешь JdbcTransactionFactory
который фиксирует в базе данных. Вы должны отслеживать, что происходит на commit
чтобы быть уверенным.
Вы проверили, что проблема не на стороне читателя? Например, он может использовать длинную транзакцию с сериализованным уровнем изоляции, в этом случае он не сможет прочитать изменения в базе данных.
В postgres, если используется репликация и реплики используются для запросов на чтение, программа чтения может увидеть устаревшие данные, даже если коммит успешно завершен на master. Я не очень знаком с оракулом, но кажется, что при использовании репликации вы можете увидеть ту же проблему:
Снимок таблицы - это согласованное с транзакцией отражение ее основных данных, поскольку эти данные существовали в определенный момент времени. Чтобы данные моментального снимка были относительно актуальными с данными его мастера, Oracle должен периодически обновлять моментальный снимок
Я бы проверил настройку БД, чтобы узнать, так ли это. Если репликация используется, вам нужно изменить свой подход к этому.