Spring Data - MongoDb - Что будет, если я обновлю дважды один и тот же документ - PullRequest
0 голосов
/ 14 апреля 2020

Я использую SpringBoot 2.1.10 и SpringData с MongoDB 3.6.2.

У меня есть класс @Service со следующими бизнес-логиками c для резервирования универсального c ваучера Document:

public Voucher reserveVoucher() {
     Voucher voucherToReserve = voucherRepository.findFirstByStatusEquals(VoucherStatus.ACTIVE)
        .orElseThrow(() -> new BadRequestException("VOUCHER_NOT_FOUND", "Voucher with status ACTIVE not found"));

     voucherToBeConsume.setStatus(VoucherStatus.RESERVED);
     voucherToBeConsume.setUserId(voucherConsumer.getUserId());

     return voucherRepository.save(voucherToBeConsume);
}

Я знаю, что @Service классов в Spring по умолчанию Singleton. Что происходит в среде с большим количеством серверов, если метод findFirstByStatusEquals извлекает один и тот же документ и после того, как метод save выполняется дважды? Документ обновляется дважды или второе обновление завершается неудачно?

1 Ответ

1 голос
/ 14 апреля 2020

Даже на одном сервере сервис может быть вызван двумя разными потоками одновременно, например, через 2 одновременных вызова REST.

В MongoDB вы не можете использовать блокировки строк, такие как SELECT ... FOR ОБНОВЛЕНИЕ в транзакционных базах данных. Так что в вашем случае документ, возможно, будет обновлен дважды.

Однако вы можете эмулировать блокировки строк с помощью условного обновления:

UpdateResult updateResult = mongoTemplate.updateFirst(
  query(
    where("_id").is(voucherToReserve.getId())
     .and("status").is(VoucherStatus.ACTIVE)
  ), 
  new Update()
    .set("status", VoucherStatus.RESERVED)
    .set("userId", userId),
  Voucher.class
);  
if(updateResult.getModifiedCount() != 1){
  throw ...
}

См. Также https://www.mongodb.com/blog/post/how-to-select--for-update-inside-mongodb-transactions

...