я тестирую какой-то небольшой банковский сервис i-1019 * с базой данных mysql.
я использую спящий режим кэша 2-го уровня (возможный источник проблемы?).
структура данных в основном состоит из 2 таблиц, таблицы для списка транзакций (депозитов) и таблицы для хранения последнего остатка для каждой учетной записи, которая должна обновляться одновременно.
и пока я тестирую параллелизм, я вижу некоторые ошибки в моей программе. вот мой код, он не идеален, но на самом деле работает.
@Service
@Transactional
public class depositService {
//method below is being executed concurrently
public Deposit save(Long userId, BigDecimal amount){ //add deposit transaction into transaction list and update related user balance
DepositTransaction depositTransaction = new DepositTransaction();
depositTransaction.setUserId(userId);
depositTransaction.setAmount(amount);
DepositTransaction depositTransactionLatest = depositTransactionRepository.findTopWithLockByUser_IdOrderByIdDesc(userId).orElse(new DepositTransaction());
depositTransaction.setBalance(depositTransactionLatest.getBalance().add(depositTransaction.getAmount())); // balance increment works well
depositTransaction = depositTransactionRepository.save(depositTransaction); //add into transaction table works as expected without any inconsistencies / lost update
AccountBalance accountBalance = accountBalanceRepository.findOneWithLockById(depositTransaction.getUserId()).orElse(null); //update user balance
accountBalance.setBalance(accountBalance.getBalance().add(depositTransaction.getAmount())); //most of the time, accountBalance.getBalance still having outdated value
accountBalanceRepository.save(companyBank);
}
}
@Repository
public interface DepositTransactionRepository{
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<DepositTransaction> findTopWithLockByUser_IdOrderByIdDesc(Long userId);
}
@Repository
public interface AccountBalanceRepository{
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<AccountBalanceTransaction> findOneWithLockById(Long userId);
}
на основе приведенного выше кода, у меня есть транзакция, и внутри нее я использую 2 pessimisti c блокировки выборки для 2 лица. сгенерированные запросы содержат «для обновления» правильно и заблокированы должным образом, так как я пытался получить блокировку, и мне не удалось использовать mysql командную строку.
моя проблема заключается в том, что при наличии одновременного запроса баланс счета будет иметь несоответствия, но я не совсем понимаю, почему блокировка депозитной транзакции может это сделать.
accountBalance.getBalance () всегда будет иметь устаревшее значение и ему придется ждать несколько итераций, чтобы изменить его на обновленное значение.
Я могу сделать эту работу, если я использую сериализованную изоляцию транзакции с механизмом повтора в случае тупика. но я предпочитаю пессимисты c, если это возможно.