Лучший способ повторить транзакцию с Seam & Hibernate - PullRequest
3 голосов
/ 08 июля 2011

У меня есть веб-приложение Seam, работающее с Seam & Hibernate (JDBC to SQLServer).

Работает хорошо, но при большой нагрузке (стресс-тест с JMeter) у меня есть LockAcquisitionException илиOptimisticLockException.

Причина LockAquisitionException вызвана транзакцией SQLServerException "(идентификатор процесса 64), которая была заблокирована для ресурсов блокировки другого процесса и была выбрана в качестве жертвы тупика. Перезапустите транзакцию".

Затем я написал Seam Interceptor для повторного выполнения таких транзакций для LockAquisitionException:

@AroundInvoke
public Object aroundInvoke(final InvocationContext invocationContext) throws Exception {
    if (instanceThreadLocal.get() == null && isMethodInterceptable(invocationContext)) {
        try {
            instanceThreadLocal.set(this);

            int i = 0;
            PersistenceException exception = null;
            do {
                try {
                    return invocationContext.proceed();
                } catch (final PersistenceException e) {
                    final Throwable cause = e.getCause();
                    if (!(cause instanceof LockAcquisitionException)) {
                        throw e;
                    }
                    exception = e;
                    i++;
                    if (i < MAX_RETRIES_LOCK_ACQUISITION) {
                        log.info("Swallowing a LockAcquisitionException - #0/#1", i, MAX_RETRIES_LOCK_ACQUISITION);
                        try {
                            if (Transaction.instance().isRolledBackOrMarkedRollback()) {
                                Transaction.instance().rollback();
                            }
                            Transaction.instance().begin();
                        } catch (final Exception e2) {
                            throw new IllegalStateException("Exception while rollback the current transaction, and begining a new one.", e2);
                        }
                        Thread.sleep(1000);
                    } else {
                        log.info("Can't swallow any more LockAcquisitionException (#0/#1), will throw it.", i, MAX_RETRIES_LOCK_ACQUISITION);
                        throw e;
                    }
                }
            } while (i < MAX_RETRIES_LOCK_ACQUISITION);

            throw exception;

        } finally {
            instanceThreadLocal.remove();
        }
    }
    return invocationContext.proceed();
}

Первый вопрос: Как вы думаете, этот перехватчик будет правильно делать эту работу?

Погуглив и увидев, что Alfresco ( с форумом поговорите здесь ), Bonita и Orchestra также имеют некоторые методы для повторного запуска таких транзакций, и они ловят гораздо больше исключений, например, StaleObjectStateException (причина моего OptimisticLockException).

Мой второй вопрос: для StaleObjectStateException () строка была обновлена ​​или удалена другимошибка транзакции (или сопоставление несохраненного значения было неверным) "), обычно вы не можете просто повторно выполнить транзакцию, так как это проблема синхронизации с базой данных и полями @Version, не так ли?Почему, например, Alfresco пытается повторно запустить такие транзакции, вызванные такими исключениями?

РЕДАКТИРОВАТЬ: Для LockAcquisitionException, вызванной SQLServerException, я просмотрел некоторые ресурсы в Интернете, и даже если мне нужно удвоитьпроверьте мой код, кажется, что он может произойти в любом случае ... вот ссылки:

Даже Microsoft говорит «Несмотря на то, что взаимные блокировки можно свести к минимуму, их нельзя полностью избежать. Именно поэтому интерфейсное приложение должно быть разработано для обработки взаимных блокировок».

1 Ответ

2 голосов
/ 20 июля 2011

На самом деле я наконец-то нашел способ увернуться от знаменитой "Транзакция (идентификатор процесса 64) была заблокирована на ресурсах блокировки с другим процессом и была выбрана в качестве жертвы тупика. Повторите транзакцию" .

Так что я не буду отвечать на мой вопрос, но я объясню, что я видел и как мне это удается.

Сначала я подумал, что у меня возникла «проблема повышения блокировки», которая преобразует мои блокировки строк в блокировки страниц и выдает мои взаимные блокировки (мой тест JMeter выполняется по сценарию, который удаляет / обновляет при выборе строк, но удаляет и обновления не обязательно относятся к тем же строкам, что и select).

Итак, я прочитал Повышение блокировок в SQL2005 и Как решить проблемы блокирования, вызванные повышением блокировок в SQL Server (от MS) и, наконец, Диагностировать производительность SQL Server проблемы с использованием sp_lock .

Но прежде чем попытаться определить, не был ли я в ситуации с эскалацией блокировки, я попадаю на эту страницу: http://community.jboss.org/message/95300. В ней говорится о " изоляции транзакции ", и у SQLServer есть специальный, который называется " изоляция моментального снимка ".

Затем я нашел Использование изоляции моментальных снимков с SQL Server и Hibernate и прочитал Использование изоляции моментальных снимков (от MS).

Итак, я сначала включил «режим изоляции моментальных снимков» в моей базе данных:

ALTER DATABASE [MY_DATABASE]
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE [MY_DATABASE]
SET READ_COMMITTED_SNAPSHOT ON

Затем я должен был определить изоляцию транзакции для драйвера JDBC для 4096 ... и, прочитав книгу "Hibernate в действии" в пункте "5.1.6 Установка уровня изоляции", он читает:

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

Итак, я прочитал Настройка источников данных JDBC (для JBoss 4) и, наконец, отредактировал мой database-ds.xml файл, добавив его:

<local-tx-datasource>
    <jndi-name>myDatasource</jndi-name>
    <connection-url>jdbc:sqlserver://BDDSERVER\SQL2008;databaseName=DATABASE</connection-url>
    <driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
    <user-name>user</user-name>
    <password>password</password>
    <min-pool-size>2</min-pool-size>
    <max-pool-size>400</max-pool-size>
    <blocking-timeout-millis>60000</blocking-timeout-millis>
    <background-validation>true</background-validation>
    <background-validation-minutes>2</background-validation-minutes>
    <idle-timeout-minutes>15</idle-timeout-minutes>
    <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
    <prefill>true</prefill>
    <prepared-statement-cache-size>75</prepared-statement-cache-size>
    <transaction-isolation>4096</transaction-isolation>
</local-tx-datasource>

Самая важная часть, конечно, <transaction-isolation>4096</transaction-isolation>.

А потом у меня больше нет проблем с тупиком! ... так что мой вопрос сейчас более или менее бесполезен для меня ... но, возможно, у кого-то может быть реальный ответ!

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