Гибернация SpringBoot Предотвращает многопоточное обновление

#multithreading #spring-boot #hibernate #jpa

#многопоточность #весенняя загрузка #спящий режим #jpa

Вопрос:

У меня есть счетчик в базе данных, который необходимо обновить, чтобы поддерживать общее количество «загрузок». Этот счетчик будет обновляться каждый раз, когда пользователь загружает файл. Счетчик создается на основе имени файла. Мы создаем новую запись в базе данных, если имени там нет со значением счетчика 1, иначе обновим ее.

В соответствии с развертыванием у нас запущены два экземпляра одних и тех же приложений.

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

Поток 1 :

 currentCounter = 1
updateOperation = 1   1 = 2
  

Поток 2 (в то же время) :

 currentCounter = 1
updateOperation = 1   1 = 2
Expected updateOperation = 2   1 = 3
  

Это будет еще большей проблемой, когда у меня будут запущены два экземпляра.

Ответ №1:

Я предлагаю использовать объект регистрации событий

 @Entity
public class EventRegistration {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String eventName;
    LocalDateTime happenedAt;
    boolean handled;

    @Version
    Long version;

    public EventRegistration(String eventName) {
        this.eventName = eventName;
        happenedAt = LocalDateTime.now();
        handled = false;
    }

    public void setAsHandled() {
        handled = true;
    }
     
    // getters, equals, hascode, toString etc...
}
  

Вы можете создавать новую запись EventRegistration для каждого события. Затем обновите счетчик событий по таймеру, добавив количество необработанных EventRegistration секунд и установив handled = true или даже удалите обработанные повторные записи

 @Autowired
EventRegistrationRepository repository;

@Transactional
public void updateEventCounter(String eventName) {
     List<EventRegistration> registrations = repository.getAllByEventNameAndHandled(eventName, false);

     //update event counter

     registrations.forEach(EventRegistration::setAsHandled);
     repository.save(registrations);
}