Mybatis SQL-сеанс фиксируется, по-видимому, медленнее, чем следующий код - PullRequest
0 голосов
/ 31 октября 2018

Фон

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

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

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

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

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

Сервис 2:

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

  2. Загрузить файл из базы данных (по идентификатору)

  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

Проблема

Время от времени случается, что Служба 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);
}

1 Ответ

0 голосов
/ 31 октября 2018

Возможно ли, что MQ-вызов может быть быстрее, чем фиксация базы данных?

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

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

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

Может ли проблема быть Mybatis? Я прочитал некоторые проблемы, касающиеся транзакций / сеансов Mybatis, но это не похоже на мою проблему

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

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

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

Снимок таблицы - это согласованное с транзакцией отражение ее основных данных, поскольку эти данные существовали в определенный момент времени. Чтобы данные моментального снимка были относительно актуальными с данными его мастера, Oracle должен периодически обновлять моментальный снимок

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...