Аксон выбрасывает исключение LockAcquisitionFailedException

0

Вопрос

Я использую фреймворк axon с версией spring boot

compile("org.axonframework:axon-spring-boot-starter:4.3.3") {
    exclude group: 'org.axonframework', module: 'axon-server-connector'
}

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

Command 'com.example.MyCommand' resulted in org.axonframework.common.lock.LockAcquisitionFailedException(Failed to acquire lock for aggregate identifier(AGG_ID), maximum attempts exceeded (100))

Это начало происходить внезапно и со временем становилось все более частым.

Агрегат настроен на использование моментальной съемки

@Bean public SnapshotTriggerDefinition MyAggregateSnapshotTriggerDefinition(Snapshotter snapshotter) { 
   return new EventCountSnapshotTriggerDefinition(snapshotter, 200); 
}

У этого агрегата есть только несколько запущенных экземпляров, и они остаются живыми в течение очень длительного периода времени (лет).

Я читал, что это исключение возникает, если процесс, по-видимому, слишком долго удерживает блокировку агрегата, в то время как команда запросила блокировку и отправила время ожидания.

Агрегат не содержит большого объема данных

@Aggregate(snapshotTriggerDefinition = "MyAggregateSnapshotTriggerDefinition")
public class MyAggregate {
   @AggregateIdentifier
   private String aggId;
   private boolean paused;
   private int pausingChangelist;
   private RequestCause pauseCause; //enum
   private Seat seat;
   private Queue<Reservation> reservationQueue;
   private boolean canPauseBuildPool;

  ...

}

НИ одна из команд, отправленных в этот агрегат, не отправляется в режиме “sendAndWait".

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

С другой стороны, обработчики источников событий выполняют некоторую логику. Они манипулируют очередью резервирования, опрашивая и вставляя резервирования.

@EventSourcingHandler
public void on(CertainEvent event) {
    // poll from queue if not empty
    // raise SeatReservedEvent
}

@EventSourcingHandler
public void on(SeatReservedEvent event) {
    // reserve seat
}

@EventSourcingHandler
public void on(SeatFreedEvent event) {
    //  free the seat 
    // poll from queue
    // if queue not empty -> raise SeatReservedEvent  
}

@EventSourcingHandler
public void on(SeatReservationQueuedEvent event) {
    // add to queue
}

Странная вещь, я также проверил другие сообщения, в которых выбрасывается такое же исключение, и, похоже, все они имеют одно и то же сообщение об ошибке, но только у меня другое количество попыток (100)

LockAcquisitionFailedException: 
Failed to acquire lock for aggregate identifier(AGG_ID), 
maximum attempts exceeded (2147483647)

Я прочитал код PessimisticLockFactory и смог понять, что это число (2147483647) представляет собой количество раз, когда процесс пытался получить блокировку на агрегате.

Почему 100 только в моем случае? (С моей стороны не было добавлено никакой дополнительной конфигурации)

Как я могу решить эту проблему? как я могу отслеживать блокировки на агрегате? как узнать, какой процесс получил токен и не выпустил его?

axon java spring
2021-10-20 21:47:19
2
0

Фактически, это исключение возникает, когда агрегат заблокирован (возможно, из-за какой-то дорогостоящей операции?), и другой поток также пытается заблокировать его.

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

Для логики вы можете проверить метод блокировки. Для значений по умолчанию вы можете ознакомиться со значениями по умолчанию конструктора.

На вопрос, почему это происходит, судя по вашему описанию, похоже, что ваш @EventSourcingHandlers делают "слишком много". Это уже кодовый запах для меня, как @EventSourcingHandlers никогда не должны применять новые события! Они реагируют на события прошлого, и им не следует "писать новую историю".

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

КР.,

2021-10-21 08:37:17

Здравствуйте, вы знаете, почему он пытался приобрести 100 раз, а в других случаях использования он пытался 2147483647 раз?
Nader Kahwaji

Я согласен с тем, что со стороны обработчиков источников событий создание событий сбивает с толку и является плохой практикой
Nader Kahwaji

Я не... значения по умолчанию acquireAttempts = 6000 и lockAttemptTimeout = 10 в то время как maximumQueued является Integer.MAX_VALUE (2147483647, который вы где-то видели). Вот откуда может возникнуть путаница.
Lucas Campos

Я проверил код для класса PessimisticLockFactory, и значение для acquireAttempts равно 100, а lockattemptimeout равно 600. Есть ли способ увеличить его?
Nader Kahwaji
0

Почему 100 только в моем случае? (С моей стороны не было добавлено никакой дополнительной конфигурации)

Похоже, в нем была ошибка 4.3.x и более ранние версии. В сообщении показано, что maximumQueued и не тот acquireAttempts. Эта фиксация исправляет это, поэтому при обновлении до последней версии Axon Framework вы должны увидеть правильное значение.

2021-10-22 10:57:16

Я проверил декомпилированный класс PessimisticLockFactory, и зарегистрированной переменной действительно является acquireAttempts. Это означает, что код уже содержит исправление.
Nader Kahwaji

Советуете ли вы увеличить количество попыток приобретения? если да, то как мы можем это сделать?
Nader Kahwaji

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................