Коммит сеанса mybatis sql кажется медленнее, чем следующий код

Фон

У нас есть 2 сервиса, написанные на Java: одна обрабатывает операции с базой данных с разными файлами (CRUD в базе данных), а другая обрабатывает длительную обработку этих записей (сложные фоновые задачи). Проще говоря, они производители и потребители.

Предполагаемое поведение выглядит следующим образом:

Сервис 1 (использует приведенный ниже код):

  1. Сохранить файл в БД

  2. Если файл имеет тип 'C', поместите его в очередь сообщений для дальнейшей обработки.

Услуга 2:

  1. Получить сообщение из очереди сообщений

  2. Загрузить файл из базы (по ID)

  3. Выполните дальнейшую обработку

Код Сервиса 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

Проблема

Время от времени случается, что Service 2 получает сообщение от MQ, пытается прочитать файл из базы данных и, что удивительно, файла там нет. Инцидент случается довольно редко, но бывает. Когда мы проверяем базу данных, файл там. Это почти похоже на то, что фоновая обработка файла началась до того, как файл был помещен в базу данных.

Вопросов

Возможно ли, что вызов MQ может быть быстрее, чем фиксация базы данных? Я создал файл в БД, назвав его коммитом, и только после этого помещаю сообщение в 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);
}

Вы проверили, выполняет ли sqlSession.commit фиксацию в базе данных? Как настроен sessionFactory? Какой конкретно тип transactionManager вы используете? Покажите, пожалуйста, реализацию oracleFileStore.create.

Roman Konoval 31.10.2018 15:05

Я добавил больше кода, хотя не уверен, насколько глубже я могу пойти с анализом.

Miroslav 31.10.2018 16:08
0
2
1 002
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Is it possible that MQ call could be faster than the database commit?

Если фиксация базы данных выполнена, изменения будут в базе данных. После этого происходит создание задачи в очереди. Главное здесь то, что вам нужно проверить, что фиксация происходит синхронно, когда вы вызываете commit в сеансе. Судя по предоставленной вами конфигурации, все в порядке, если только в самом Connection нет каких-либо проблем. Я могу представить, например, что есть некоторая обертка над родным Connection. Я бы проверил в отладчике, что вызов commit вызывает вызов Connection.commit в реализации из драйвера Oracle JDBC. Еще лучше проверить логи на стороне БД.

Does the connection needs to be closed to be sure the commit was performed? I always thought when I commit then it's in the database regardless if my transaction ended or not.

Ты прав. Нет необходимости закрывать соединение, которое подчиняется спецификации JDBC (это делает собственное соединение JDCB). Конечно, вы всегда можете создать некую оболочку, которая не подчиняется API Connection и делает некоторую магию (например, задерживает фиксацию до закрытия соединения).

Can the problem be Mybatis? I've read some problems regarding Mybatis transactions/sessions but it doesn't seem similar to my problem

Я бы сказал, что это маловероятно. Вы используете JdbcTransactionFactory, который выполняет фиксацию в базе данных. Вам нужно отслеживать, что происходит на commit, чтобы быть уверенным.

Вы проверили, что проблема не на стороне читателя? Например, он может использовать длинную транзакцию с сериализованным уровнем изоляции, в этом случае он не сможет прочитать изменения в базе данных.

В postgres, если используется репликация и реплики используются для запросов чтения, читатель может видеть устаревшие данные, даже если фиксация успешно завершена на главном сервере. Я не так хорошо знаком с oracle, но с похоже, что это, если используется репликация, вы можете увидеть ту же проблему:

A table snapshot is a transaction-consistent reflection of its master data as that data existed at a specific point in time. To keep a snapshot's data relatively current with the data of its master, Oracle must periodically refresh the snapshot

Я бы проверил настройку БД, чтобы узнать, так ли это. Если используется репликация, вам необходимо изменить свой подход к этому.

Спасибо за отличный ответ, это то, что я искал. Эти идеи продвигают меня в моих исследованиях. Afaik наше соединение стандартное Соединение с пулом c3p0, но я лучше его проверю, это немного темная часть кода. Идея репликации действительно возможна. Наш провайдер БД печально известен своими странными махинациями с базой данных, поэтому я не удивлюсь, если это будет правдой. Я получу некоторую информацию и напишу здесь обновление

Miroslav 31.10.2018 17:12

Другие вопросы по теме