У меня есть метод, который выполняет тяжелые операции (обработка файлов), который не требует транзакции, но находится внутри нее и генерирует тайм-ауты. У меня нет возможности реорганизовать код или увеличить время ожидания транзакции по умолчанию. Я попытался использовать TransactionAttributeType.NOT_SUPPORTED
в методе обработчика файлов, чтобы временно приостановить транзакцию, но он продолжает считать время, пока он приостановлен, и продолжает давать мне тайм-аут.
Такой способ работы правильный? Когда транзакция приостановлена, время транзакции все еще тратится?
Я сделал следующий пример приближения с той же проблемой. Код:
@Slf4j
@Stateless
public class Foo1 {
@Inject
Foo2 foo2;
@PersistenceContext
EntityManager em;
@TransactionTimeout(value= 10, unit = TimeUnit.SECONDS)
public void saveWith10secondsTimeout() {
log.info("Start Foo1");
Foo entity = new entity();
em.persist(entity);
foo2.threadSleep15seconds();
log.info("End Foo1");
}
}
@Slf4j
@Stateless
@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)
public class Foo2 {
public void threadSleep15seconds() {
log.info("Start Foo2");
try {
Thread.sleep(15 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("End Foo2");
}
}
Вызов диспетчера:
@Inject
Foo1 foo1;
@GET
@Path("/foo-not-supported")
@TransactionAttribute(TransactionAttributeType.NEVER)
public Response testNotSupported() {
log.info("Start testNotSupported");
foo1.saveWith10secondsTimeout();
return Response.status(OK).build();
}
Журналы:
15:00:30,867 INFO [com.FooController] (default task-1) Start testNotSupported
15:00:30,868 INFO [com.Foo1] (default task-1) Start Foo1
15:00:30,883 INFO [com.Foo2] (default task-1) Start Foo2
15:00:40,867 INFO [com.arjuna.ats.arjuna] (Transaction Reaper) ARJUNA012117: TransactionReaper::check timeout for TX 0:ffff7f000101:-441cac6:64394e3c:382 in state RUN
15:00:40,877 INFO [org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorTrackingImpl] (Transaction Reaper Worker 0) HHH000451: Transaction afterCompletion called by a background thread; delaying afterCompletion processing until the original thread can handle it. [status=4]
15:00:40,881 INFO [com.arjuna.ats.arjuna] (Transaction Reaper Worker 0) ARJUNA012121: TransactionReaper::doCancellations worker Thread[Transaction Reaper Worker 0,5,main] successfully canceled TX 0:ffff7f000101:-441cac6:64394e3c:382
15:00:45,884 INFO [com.Foo2] (default task-1) End Foo2
15:00:45,884 INFO [com.Foo1] (default task-1) End Foo1
15:00:45,884 INFO [com.arjuna.ats.arjuna] (default task-1) ARJUNA012077: Abort called on already aborted atomic action 0:ffff7f000101:-441cac6:64394e3c:382
15:00:45,887 ERROR [org.jboss.as.ejb3.invocation] (default task-1) WFLYEJB0034: EJB Invocation failed on component Foo1 for method public void com.Foo1.saveWith10secondsTimeout(): javax.ejb.EJBTransactionRolledbackException: javax.transaction.RollbackException: WFLYEJB0447: Transaction 'Local transaction (delegate=TransactionImple < ac, BasicAction: 0:ffff7f000101:-441cac6:64394e3c:382 status: ActionStatus.ABORTED >, owner=Local transaction context for provider JBoss JTA transaction provider)' was already rolled back
@Chris Цель приостановки транзакции состояла в том, чтобы избежать тайм-аута, некоторые процессы, обрабатывающие файлы, близки к пяти минутам (в примере это будет метод threadSleep15seconds), и они генерируют тайм-аут в транзакции, но операция с файлом проведен правильно. Идея заключалась в том, чтобы использовать NOT_SUPPORTED, чтобы это время не добавлялось ко времени транзакции, но либо я его не правильно использую, либо оно так не работает и у меня не будет выбора, кроме как увеличить таймаут транзакции или рефакторить код .
При использовании TransactionAttributeType.NOT_SUPPORTED текущие транзакции приостанавливаются во время выполнения метода и возобновляются после завершения. Тайм-аут транзакции все еще работает, поэтому, если он истекает до завершения метода, транзакция откатывается. В этом случае транзакция откатывается до завершения метода, потому что Foo2.threadSleep15seconds() занимает больше 10-секундного времени ожидания, установленного для Foo1.saveWith10secondsTimeout(). Вы можете увеличить значение времени ожидания или обрабатывать файловые операции асинхронно или небольшими порциями, если вы не можете изменить код.
Кажется, здесь есть какая-то проблема с дизайном.
Foo2.threadSleep15seconds()
, похоже, имитирует длительный файловый процесс, о котором вы говорите. Насколько тесно эта обработка файла связана с текущей транзакцией БД?
Если у вас может быть какое-то отдельное управление состоянием, то, как часть Foo1.saveWith10secondsTimeout()
, вы можете просто вставить запись в таблицу БД, которая обрабатывает файл pending
. И вернуть сразу, чтобы сделка не задержалась на долгий срок.
Создайте отдельный поток (асинхронное выполнение Foo2.threadSleep15seconds()
) для обработки всех файлов, которые будут обрабатываться всеми запросами, сделанными к Foo1.saveWith10secondsTimeout()
. Этот поток (или пул потоков) будет продолжать обрабатывать файлы и изменять статус этих файлов в таблице БД с pending
на done
или error
в зависимости от результата, а затем запускать следующий процесс на основе результата.
Но если одна транзакция задерживается до тех пор, пока не будут обработаны все файлы, это определенно заблокирует некоторые другие действия в другом месте.
Тайм-аут транзакции означает, что транзакция может быть отменена, если она все еще активна по истечении периода. Приостановка транзакции не влияет на тайм-аут (поскольку это противоречит цели установки тайм-аута).
Я надеюсь, что эта транзакция может удерживать блокировки, препятствующие завершению других транзакций. Не лучше держать транзакции открытыми в течение длительного времени, и просто возможность отключить транзакцию ничего не делает для освобождения ее блокировок и продолжения других процессов. С какой именно целью вы приостанавливаете транзакцию?