Я использую фреймворк 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 только в моем случае? (С моей стороны не было добавлено никакой дополнительной конфигурации)
Как я могу решить эту проблему? как я могу отслеживать блокировки на агрегате? как узнать, какой процесс получил токен и не выпустил его?