Для простоты возьмем таблицу базы данных storage
в моем Postgres с одним полем resources
. При использовании запланированной задачи объем ресурсов для нескольких записей постоянно обновляется следующим образом:
@Scheduled(fixedDelay = 500)
@Transactional
public void tick() {
// ...
storageService.findAll().stream()
// This method call in map is highly simplified for this post
// Assume it increments all storages by one per tick of the scheduled method
.map(storage -> storage.withResources(storage.getResources() + 1))
.forEach(storageService::update);
// ...
}
Соответствующая служба основана на DAO, сгенерированных jOOQ:
@Service
@RequiredArgsConstructor
public class StorageServiceImpl implements StorageService {
private final StorageDao dao;
@Override
public List<Storage> findAll() {
return dao.findAll();
}
@Override
public void update(final Storage storage) {
dao.update(storage);
}
}
Исходный фрагмент работает в предположении, что между вызовами findAll
и update
не происходит никаких изменений в выбранных строках (в основном все в этой конкретной таблице), в противном случае возникает аномалия и ресурсы, скорее всего, отсутствуют.
Теперь у меня есть контроллер, который предоставляет конечную точку для расходования этих хранимых ресурсов. Я уже реализовал блокировку Redis для предотвращения множественных параллельных вызовов этой конечной точки. В этой цепочке операций на этапе выделения ресурсов, которые необходимо потратить, я аналогичным образом сокращаю количество ресурсов. Что-то вроде этого:
@Transactional
// ...
storageService.findById(id) // Optional<Storage>
.map(storage -> storage.withResources(storage.getResources() - costs)
.map(storageService::update)
.orThrow(IllegalStateException::new);
В этот момент мне было любопытно, работает ли (вообще) это правильно, параллельно с моей запланированной задачей или нет. В основном ставлю под сомнение мое использование @Transactional
. Как я могу проверить правильность? Или необходимо вручную написать запрос jOOQ для блокировки строк с помощью SELECT ... FOR UPDATE
?