Cześć, mam taki case, gdzie upraszczając problem do minimum posiadam tabelę z kolumnami id oraz counter. Muszę uruchamiać pewną logikę, a następnie inkrementować licznik. Równoległa transakcja, która również chce zrobić to samo powinna poczekać, aż ta 1 transakcja zrobi swoją logikę i zupdatuję licznik. Nałożyłem zatem na metodę findById() locka PESSIMISTIC_WRITE i po jej wywołaniu mam locka na wierszu z licznikiem i ta druga transakcja czeka. Pojawiają się jednak pewien problem:
Pierwsza transakcja sprawdza sobie, że jeszcze nie istnieje row z takim id, robi saveAndFlush(), następnie robi select for update, czyli findById(), robi logikę, inkrementuje licznik i wywołuje save(). Jeżeli w momencie wykonywania dłuższej logiki przez 1 transakcję wywołam drugą transakcję to metoda existById powie, że obiektu nie ma, ale saveAndFlush już rzuca wyjątek, bo istenieje taki obiekt.
Nie mam pomysłu jak to naprawić. Może kwestia propagacji transakcji ?
@Transactional
public void foo() {
final String id = "ID";
if (!fooRepository.existsById(id)) {
fooRepository.saveAndFlush(new Foo(id, 0));
}
fooRepository.findById(id) // findById ma locka PESSIMISTIC_WRITE
.ifPresent(this::someLogic);
}
private void someLogic(final Foo foo) {
try {
Thread.sleep(5000); // imitacja jakiejś dłuższej logiki
} catch (InterruptedException e) {
e.printStackTrace();
}
foo.increment();
fooRepository.save(foo);
}